HttpClient请求间隔超80秒后执行耗时变长原因排查
问题现象
- 两次HTTP请求间隔小于80秒时,单次请求执行耗时稳定在400-700ms区间
- 两次HTTP请求间隔超过80秒时,请求执行耗时升至1000-1500ms,耗时水平与首次发起请求基本一致
- 该问题会导致连续80秒以上无用户访问的Telegram机器人,收到新请求时响应延迟明显升高
复现代码
using System.Diagnostics; var httpClient = new HttpClient(); var watch = new Stopwatch(); watch.Start(); var data1 = await httpClient.GetAsync("https://google.com"); watch.Stop(); Console.WriteLine($"{data1.StatusCode} {watch.ElapsedMilliseconds}"); watch.Reset(); Thread.Sleep(10000); watch.Start(); var data2 = await httpClient.GetAsync("https://google.com"); watch.Stop(); Console.WriteLine($"{data2.StatusCode} {watch.ElapsedMilliseconds}");
测试结果
- 代码中
Thread.Sleep参数设为10000(请求间隔10秒)时,控制台输出:OK 1319 | OK 457 - 代码中
Thread.Sleep参数设为85000(请求间隔85秒)时,控制台输出:OK 1399 | OK 1183
产生原因
该现象是HTTP长连接复用机制与空闲连接超时回收规则共同作用的结果:
- 首次发起HTTP请求时,客户端需要完成完整的TCP三次握手、TLS握手流程,这部分固定网络交互开销占总耗时的60%以上,因此首次请求耗时普遍在1000ms以上。
- 请求完成后,已建立的TCP连接不会被立即释放,会存入HttpClient连接池保持空闲状态;后续短时间内发起同域名请求时,可直接复用池内现成连接,省去握手环节开销,因此请求耗时降至400-700ms区间。
- 客户端运行时、服务端网关、中间链路网络设备均配置了空闲连接回收规则:当TCP连接连续空闲超过阈值(当前场景下阈值约为80秒),连接会被主动断开回收。
- 请求间隔小于80秒时,池内连接仍处于可用状态,可直接复用,耗时较低
- 请求间隔超过80秒时,原有空闲连接已被回收,发起请求时需要重新完成完整的TCP+TLS握手建立新连接,耗时回到首次请求水平
优化方案
可根据项目使用的.NET版本选择对应方案解决:
- 启用HTTP KeepAlive探活(.NET 5及以上版本支持):通过
SocketsHttpHandler配置连接空闲时自动发送探活包,避免连接被链路节点或服务端判定为空闲回收,配置示例:
var handler = new SocketsHttpHandler { KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always, KeepAlivePingDelay = TimeSpan.FromSeconds(60), KeepAlivePingTimeout = TimeSpan.FromSeconds(10) }; var httpClient = new HttpClient(handler);
- 定时预热连接:低版本.NET可在后台运行定时任务,每隔70秒向目标API发送一个轻量请求,保持连接池内的长连接持续活跃,避免被回收。
内容的提问来源于stack exchange,提问作者Роман
相关产品推荐
相关产品推荐

