首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

Vert.x HttpClient调用后端服务时使用Idle Timeout和KeepAlive Timeout的行为分析

编程知识
2024年09月12日 20:08

其实网上有大量讨论HTTP长连接的文章,而且Idle Timeout和KeepAlive Timeout都是HTTP协议上的事情,跟Vert.x本身没有太大关系,只不过最近在项目上遇到了一些问题,用到了Vert.x的HttpClient,就干脆总结一下,留给自己今后做参考。

在使用Vert.x的HttpClient的时候,可以使用HttpClientOptions配置KeepAlive Timeout以及Idle Timeout的行为,本文将讨论在Vert.x HttpClient中Idle Timeout的设置、如何启用和禁用KeepAlive,以及如果启用KeepAlive,其超时设置(Timeout)对HTTP连接保活的影响。

需要注意的是,这是客户端的设置,服务端由于实现技术多样,这里不深入讨论,本文默认服务端已经开启了KeepAlive(事实上也是大多数HTTP服务器的默认行为)。

本文讨论的场景仅适用HTTP/1.1的情况,HTTP/1.0处理KeepAlive方式略有不同,HTTP/1.1默认支持KeepAlive,HTTP/1.0需要显式在header里加入Connection: keep-alive。

场景演练

这里我们设定多个场景来测试不同情况下,整个系统的行为表现是什么。

场景一:HttpClient禁用 KeepAlive

HttpClientOptions里使用setKeepAlive(false)来禁用KeepAlive:

final HttpClientOptions options = new HttpClientOptions()
    .setKeepAlive(false);

此时,Vert.x Http Client会在请求头中加入connection: close,表示在完成一次HTTP request/response的流程后,服务端需要主动发起关闭连接请求:

Wireshark抓包分析:

  1. Vert.x HttpClient使用60489端口与服务端建立连接(SYN),服务端以(SYN, ACK)数据包回应,同意建立连接
  2. Vert.x HttpClient向服务端发送HTTP请求,服务端处理请求并返回
  3. 由于Vert.x HttpClient在发出HTTP请求时,使用了connection: close,所以服务端主动发起(FIN, ACK)数据包,表示服务端已关闭连接,客户端获得数据包后,返回ACK表示确认,然后也向服务端发出(FIN, ACK),表示客户端也关闭了连接,最后服务端返回ACK,表示确认

场景二:HttpClient启用KeepAlive

HttpClientOptions里使用setKeepAlive(false)来禁用KeepAlive:

final HttpClientOptions options = new HttpClientOptions()
    .setKeepAliveTimeout(15)
    .setKeepAlive(true);

 此时,Vert.x Http Client并不会发送connection: close头,因为HTTP/1.1默认使用长连接,所以无需额外指定任何请求头。在KeepAlive超时后,会由客户端主动关闭HTTP连接。

Wireshark抓包分析:

  1. Vert.x HttpClient使用60937端口与服务端建立连接(SYN),服务端以(SYN, ACK)数据包回应,同意建立连接
  2. Vert.x HttpClient向服务端发送HTTP请求,服务端处理请求并返回
  3. Vert.x HttpClient在等待了大约15秒以后(上面代码中setKeepAliveTimeout设置的15秒),由于没有新的HTTP请求需要占用连接,于是就向服务端发起(FIN, ACK)数据包,表示客户端已关闭连接,服务端获得数据包后,返回ACK表示确认,然后也向客户端发出(FIN, ACK),表示服务端也关闭了连接,最后客户端返回ACK,表示确认

如果服务端的KeepAlive Timeout大于HttpClient的KeepAlive Timeout,那么当一段时间内没有任何HTTP请求发出,在HttpClient KeepAlive首先超时前,HTTP连接可以一直被重用,直到HttpClient KeepAlive超时,由客户端发起关闭连接请求:

  1. 可以看到,客户端端口63094的连接在多次HTTP请求中一直被重用
  2. 15秒内,没有新的HTTP请求,客户端主动发起断开连接数据包

如果服务端的KeepAlive Timeout小于HttpClient的KeepAlive Timeout(比如服务端KeepAlive Timeout为10s),那么当一段时间内没有任何HTTP请求发出,服务端会首先发起关闭连接请求:

  1. Vert.x HttpClient使用64282端口与服务端建立连接(SYN),服务端以(SYN, ACK)数据包回应,同意建立连接
  2. 在多次HTTP请求过程中,HTTP连接被重用
  3. 10秒过后,服务端首先发起关闭连接请求
  4. 之后客户端再次发出HTTP请求,会重新新建一个HTTP连接

场景三:HttpClient同时使用Idle Timeout和KeepAlive Timeout

为了模拟这样的场景,在创建Vert.x HttpClient时,使用如下HttpClientOptions

final HttpClientOptions options = new HttpClientOptions()
  .setIdleTimeout(5)
  .setIdleTimeoutUnit(TimeUnit.SECONDS)
  .setKeepAliveTimeout(20)
  .setKeepAlive(true);

然后让服务端在返回HTTP Response的时候,先等待7秒钟:

[HttpGet(Name = "GetWeatherForecast")]
public async Task<IEnumerable<WeatherForecast>> Get()
{
    await Task.Delay(7000);
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
    })
    .ToArray();
}

Wireshark抓包分析:

  1. 建立连接
  2. 发送请求,服务器开始处理请求,需要7秒才能处理完
  3. 5秒后,客户端Idle Timeout了,等不起,就向服务端发起关闭连接,服务端响应关闭连接
  4. 下一次请求,需建立新的连接

因此,可以这样理解这两个设置:

  • 短的 Idle Timeout 和长的 KeepAlive Timeout: 如果 IdleTimeout 设置得比较短,而 KeepAliveTimeout 设置得比较长,连接会因为空闲超时(Idle Timeout)而关闭。因此,当新的 HTTP 请求到来时,需要建立新的连接。
  • 长的 Idle Timeout 和短的 KeepAlive Timeout: 如果 IdleTimeout 设置得比较长,而 KeepAliveTimeout 设置得比较短,在 Keep-Alive 有效时间内,即使当前连接上有请求在等待响应,该连接仍然可以接收新的 HTTP 请求,直到 Keep-Alive 超时触发。
  • 如果某个HTTP请求在某个HTTP连接上等待服务端返回,那么这个HTTP连接仍然处于活跃状态,此时并不会触发KeepAlive的倒计时

在vert.x中,RequestOptions也有类似的setIdleTimeout的方法来设置Idle Timeout,但它的作用域仅限于当前的HTTP请求,而HttpClientOptions.setIdleTimeout作用域为整个应用程序全局。

场景四:在服务端KeepAlive即将到期的时候发送HTTP请求

仍然使用上面【场景二】的环境设定,只是让客户端每隔KeepAlive Timeout相同时间发送一次请求,会发现,多数情况下,请求可以成功,但有时候会发生错误。比如,如果服务端KeepAlive Timeout为10秒,客户端也是每隔10秒发送请求:

  1. 建立连接,并发送HTTP请求,服务端正常响应
  2. 10秒后,客户端再次发出请求,服务端ACK了请求,但与此同时,服务端KeepAlive超时,于是就向客户端发送(RST, ACK)数据包用于复位连接(异常关闭连接),此时客户端就会报错:io.vertx.core.net.impl.ConnectionBase SEVERE: Connection reset

总结

  1. 一个HTTP连接是否能够重用,同时取决于客户端和服务端的KeepAlive行为
  2. 服务端通常是默认开启KeepAlive的,它也有一个默认值(不同服务端实现不一样)
  3. 客户端可以选择是否启用HTTP连接重用(启用或者禁用KeepAlive)
    1. 在HTTP/1.0中,启用时需要在Request Header中加入Connection: keep-alive,默认禁用
    2. 在HTTP/1.1中,禁用时,需要在Request Header中加入Connection: close,默认启用
  4. 如果客户端禁用,则HTTP连接不会重用,每次请求都会创建新的HTTP连接
  5. 如果客户端启用,并且客户端KeepAlive Timeout(ckt)小于服务端KeepAlive Timeout(skt),那么在min(ckt, skt)时间段内如果没有新的HTTP请求,并且HTTP连接处于空闲状态,客户端会发起HTTP连接关闭
  6. 如果客户端启用,并且客户端KeepAlive Timeout(ckt)大于服务端KeepAlive Timeout(skt),那么在min(ckt, skt)时间段内如果没有新的HTTP请求,并且HTTP连接处于空闲状态,服务端会发起HTTP连接关闭
  7. 当一个HTTP请求发送到服务端后,服务端需要一定时间处理,Vert.x HTTP Client设置Idle Timeout,可以使得当服务端在一定时间内没有响应时,客户端可以主动关闭HTTP连接。在客户端由于Idle Timeout而关闭连接之前,该HTTP请求仍在等待服务端的返回,此时承载该HTTP请求的HTTP连接仍可继续接受其它的HTTP请求
  8. 当服务端需要一定时间处理HTTP请求时,如果这个处理时间恰好与服务端KeepAlive Timeout相当,是有可能出现HTTP连接被异常关闭导致客户端报错的情况的。常见解决办法:
    1. 尽量避免在服务端KeepAlive超时时发出请求(根据实际情况调整KeepAlive参数或者定制HTTP请求发起策略)
    2. 重试机制
    3. 确保客户端和服务端超时设置合理,根据实际情况进行调整

KeepAlive Timeout和Idle Timeout的关系

两者是独立的设置,但在某些情况下可能会产生交互。例如,如果 IdleTimeout 设置的时间比 KeepAliveTimeout 短,那么连接可能会因为空闲超时而关闭,即使 Keep-Alive 允许更长时间的连接复用。

最佳实践(参考自ChatGPT)

  • 根据应用需求调整:
    • 如果应用程序需要频繁复用连接,可以设置较长的 KeepAliveTimeout
    • 如果需要防止连接长时间空闲占用资源,可以设置较短的 IdleTimeout
  • 平衡性能和资源:
    • 设置 IdleTimeout 时,确保它足够长以完成请求处理,但不要过长以免浪费资源。
    • 设置 KeepAliveTimeout 时,确保它能够有效地复用连接以提高性能。
  • 测试和监控:
    • 在实际应用中,监控连接的使用情况,并根据性能和资源使用情况调整这些超时设置。

通过合理设置这两个超时值,可以优化连接的使用效率,同时避免不必要的资源占用。

From:https://www.cnblogs.com/daxnet/p/18410025
本文地址: http://shuzixingkong.net/article/1961
0评论
提交 加载更多评论
其他文章 Qml 实现仿前端的 Notification (悬浮出现页面上的通知消息)
在前端中一般称它为 Notification 或 Message,但本质是一种东西,即:悬浮弹出式的消息提醒框。 这种组件一般具有以下特点: 1、全局/局部显示:它不依赖于具体的页面元素,可以在整个页面的任意位置显示。 2、自动消失:默认情况下,消息会在一定时间后自动消失,也可以设置为不自动消失。
Qml 实现仿前端的 Notification (悬浮出现页面上的通知消息) Qml 实现仿前端的 Notification (悬浮出现页面上的通知消息) Qml 实现仿前端的 Notification (悬浮出现页面上的通知消息)
LinkedHashMap原理详解—从LRU缓存机制说起
写在前面 从一道Leetcode题目说起 首先,来看一下Leetcode里面的一道经典题目:146.LRU缓存机制,题目描述如下: 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容
LinkedHashMap原理详解—从LRU缓存机制说起 LinkedHashMap原理详解—从LRU缓存机制说起 LinkedHashMap原理详解—从LRU缓存机制说起
搭建ipv6并发代理池
声明 本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 学习目标 ounter(lineipv6代理池学习 前置环境配置 要求linux系统。我是pve下的ubuntugolang的
搭建ipv6并发代理池 搭建ipv6并发代理池 搭建ipv6并发代理池
Java怎么把多个对象的list的数据合并
1.示例一:创建几个包含Person对象的List,并将它们合并成一个新的List 在Java中,将多个对象的List合并通常涉及到遍历这些List并将它们的元素添加到一个新的List中。这里,我将给出一个详细的代码示例,该示例将展示如何将多个包含相同类型对象的List合并成一个List。 假设我们
DECL: 针对噪声时间序列的去噪感知对比学习《Denoising-Aware Contrastive Learning for Noisy Time Series》(时间序列、对比学习、去噪)
今天是2024年9月12日,组会摸鱼,很久没看论文了,在摸鱼看代码,最近IJCAI 2024出来了,找了几篇论文看,首先这是第一篇。 论文:Denoising-Aware Contrastive Learning for Noisy Time Series 或者是:Denoising-Aware C
Go日志管理库zap
一、zap介绍 在许多Go语言项目中,我们需要一个好的日志记录器能够提供下面这些功能: 1.能够将事件记录到文件中,而不是应用程序控制台。 2.日志切割-能够根据文件大小、时间或间隔等来切割日志文件。 3.支持不同的日志级别。例如INFO,DEBUG,ERROR等。 4.能够打印基本信息,如调用文件
Go日志管理库zap Go日志管理库zap Go日志管理库zap
vue3项目部署到Github
此教程适应于以webpack,vue-cli,vite等脚手架构建的vue项目。当然,vue2和vue3都是可以滴。 1. 前提:你的代码库已经提交到Github上 如果没有的话,请到GitHub上新建仓库,并把你本地的项目提交到GitHub上新建的仓库里。 具体方法,可以参考我的博客 Git使用记
vue3项目部署到Github vue3项目部署到Github vue3项目部署到Github
项目完成小结:使用DjangoStarter v3和Taro开发的微信小程序
前言 不知不觉已经九月了,又到了一年的开学季,我每年都想做的项目墙甚至连个影子都没有…&#128514; 最近生活中的琐事太多了,导致完全没有想写文章的动力,不过再怎么拖还是得记录,随便写写吧~ 这次是7月份的一个小项目,谈不上什么技术含量,算是友情开发了。后端 DjangoStarter v3,前