Polly重试未关闭HttpClient调用引发超时异常问题
问题解答
1. 该现象是Polly与IHttpClientFactory集成时的默认正常行为
核心原因是HttpClient默认的Timeout属性统计的是整个请求执行链路的总耗时,而非单次HTTP请求的耗时。
你当前注册的重试策略是包裹在SendAsync调用外层的:从第一次发起请求开始,HttpClient就启动了超时计时,后续Polly触发的每一次重试、重试之间的等待间隔,都会被计入总耗时。只要从第一次请求发出到拿到最终响应的累计时间超过默认100s阈值,就会抛出超时异常,和单次请求的连接是否释放没有关系——你用using包裹HttpRequestMessage的写法是对的,不存在首次请求连接未关闭的问题。
之前你集成Polly超时策略不生效,是因为策略注册顺序错了:如果把超时策略放在重试策略外层,它统计的依然是包含所有重试等待的总耗时,和HttpClient默认超时的逻辑完全一致,自然解决不了问题。
2. 正确配置方案(无需将HttpClient超时设为覆盖全重试周期的极大值)
核心思路是拆分「单次请求超时」和「重试流程」两层逻辑,调整策略注册顺序,让每次重试都触发独立的超时计时,具体配置如下:
第一步:调整HttpClient超时配置
将HttpClient.Timeout设置为单次HTTP请求允许的最大耗时即可,不需要考虑重试等待的时间,比如给单次请求留3-5s的冗余就足够:
var retries = // 你的重试间隔配置,比如25次每次间隔10s serviceCollection .AddHttpClient<IApplicationApi, ApplicationApi>(client => { // 仅覆盖单次请求的超时阈值,不需要累计重试耗时 client.Timeout = TimeSpan.FromSeconds(5); }) // 注意策略注册顺序:外层重试,内层单次超时 .AddPolicyHandler(GetRetryPolicy<ApplicationApi>(retries)) .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(4)));
第二步:补全重试策略的异常捕获条件
把Polly超时抛出的TimeoutRejectedException加入重试触发条件,避免单次请求超时后直接抛出异常不重试:
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy<T>(List<TimeSpan> retries) { return HttpPolicyExtensions .HandleTransientHttpError() .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.TooManyRequests) // 新增捕获Polly单次超时异常 .Or<Polly.Timeout.TimeoutRejectedException>() .WaitAndRetryAsync(retries, onRetry: (outcome, timespan, retryAttempt, context) => { // 原有日志逻辑 }); }
配置说明
AddPolicyHandler的注册顺序是从外到内执行:先注册的策略在外层,后注册的在内层。上述配置下,每次重试触发时都会走内层全新的超时判断,不会累计之前的请求和等待耗时。- 不需要手动处理连接关闭:.NET Core 2.1及以上版本默认用
SocketsHttpHandler管理连接池,单次请求完成/超时后,对应连接会自动回收到连接池复用,不存在残留占用问题。 - 额外注意:你贴的
CallApi方法签名是无返回值的void,但内部用了await异步调用,记得将方法签名改为async Task,避免出现未捕获的异步异常。
内容的提问来源于stack exchange,提问作者purnadika
相关产品推荐
相关产品推荐

