You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure App Service调用第三方API时HttpClient随机请求失败排查

故障根因
  • 重试策略覆盖不全:默认HandleTransientHttpError()仅处理HttpRequestException、5xx状态码、408请求超时,本次故障中连接阶段抛出的TaskCanceledException(内部为SocketException)未被纳入重试规则,不会触发重试直接抛出异常。
  • 连接生命周期配置冲突:代码中同时配置了ServicePoint.ConnectionLeaseTimeout = 60s和SetHandlerLifetime = 10min,前者要求连接60秒后强制回收,后者的HttpClient处理器10分钟才会销毁,导致池子里残留大量已经被ServicePoint标记为失效的连接,复用这些连接时会直接触发连接失败。
  • Azure负载均衡空闲连接中断:Azure App Service的出站负载均衡默认空闲超时为4分钟,未开启TCP保活的空闲连接会被静默断开,复用这类过期连接时会直接触发连接失败。
  • 全局超时设置偏严:HttpClient全局超时设为10秒,如果网络抖动导致TCP三次握手耗时稍长,会直接触发超时取消。
排查方法
  • 补充异常细节日志:捕获TaskCanceledException时,判断内部是否包含SocketException,同时记录请求的完整链路ID、耗时,确认故障是否均发生在连接建立阶段。
  • 开启HttpClient调试日志:将日志配置中Microsoft.Extensions.Http的级别调整为Debug,追踪连接的创建、复用、销毁流程,确认故障请求是否复用了空闲时间超过阈值的旧连接。
  • 验证重试触发情况:在Polly的重试回调中添加日志,记录所有触发重试的异常类型、状态码,确认连接类异常是否未被重试规则捕获。
  • 底层网络连通性验证:在App Service的Kudu控制台中使用tcpping <第三方域名>:443持续压测,确认是否存在随机TCP连接失败情况,排除底层网络抖动影响。
修复方案
  • 完善Polly重试策略,显式覆盖连接阶段异常:
var retryPolicy = HttpPolicyExtensions.HandleTransientHttpError()
    // 覆盖连接阶段抛出的取消异常
    .Or<TaskCanceledException>(ex => ex.InnerException is SocketException)
    .OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
    .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) + TimeSpan.FromMilliseconds(jitter.Next(0, 500)));
  • 统一连接生命周期配置,移除过时的ServicePointManager配置,使用.NET Core推荐的SocketsHttpHandler配置:
services.AddHttpClient<IFooService, FooService>(client =>
{
    // 可根据实际情况适当放宽超时到15s
    client.Timeout = TimeSpan.FromSeconds(15);
})
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
    // 连接池中的连接最长存活1分钟,避免Azure负载均衡断连
    PooledConnectionLifetime = TimeSpan.FromMinutes(1),
    // 开启保活探测,避免空闲连接被静默断开
    KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always,
    KeepAlivePingTimeout = TimeSpan.FromSeconds(30)
})
// 处理器生命周期和连接生命周期保持一致
.SetHandlerLifetime(TimeSpan.FromMinutes(1))
.AddPolicyHandler(retryPolicy);
  • 如果需要保留ServicePointManager配置,需将SetHandlerLifetime调整为和ConnectionLeaseTimeout一致的60秒,同时开启TCP保活:
var sp = ServicePointManager.FindServicePoint(new Uri(_config.BaseUri));
sp.ConnectionLeaseTimeout = 60 * 1000;
// 空闲2分钟后开始发送保活包,每30秒一次
sp.SetTcpKeepAlive(true, 120000, 30000);

内容的提问来源于stack exchange,提问作者Rory McCrossan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 19:45:03