.NET Core应用使用Polly重试策略时遭遇100秒超时的问题排查咨询
嘿,这个问题我之前也踩过坑,咱们一步步拆解来看:
为什么会触发100秒超时?
首先明确一点:这不是你Polly使用方式的错误,而是HttpClient全局超时和重试总时长叠加导致的设计特性问题。
你的代码里,HttpClient配置了100秒的全局超时(默认或自定义配置的),这个超时的作用是整个请求生命周期的总时长限制——包括所有重试的等待时间+每次请求的实际耗时。
看你的重试策略,如果用默认的指数退避逻辑,5次重试的等待时间总和是:2+4+8+16+32=62秒。再加上每次请求本身的耗时(哪怕每次只花10秒),总时长就会达到62+5*10=112秒,直接超过100秒的全局阈值,触发超时错误。
怎么解决这个问题?
有几个实用的方案,你可以根据业务场景选择:
方案1:给单次请求配置独立超时(推荐)
不要依赖HttpClient的全局超时来限制单次请求,而是用Polly的TimeoutPolicy给每个重试的单次请求设置超时。这样可以精准控制单次请求的耗时,同时让全局超时只负责限制所有重试+等待的总时长(或者把全局超时设得更合理一些)。
修改你的ResiliencyPolicy,添加单次超时策略并和重试策略组合:
public static IAsyncPolicy<HttpResponseMessage> GetCombinedRetryAndTimeoutPolicy( int numberOfAttempts = 5, TimeSpan[] timeOfAttempts = null, TimeSpan singleRequestTimeout = default) { // 配置单次请求的超时时间,比如15秒 var singleTimeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>( singleRequestTimeout == default ? TimeSpan.FromSeconds(15) : singleRequestTimeout); // 重试策略,同时捕获单次超时的异常进行重试 var retryPolicy = HttpPolicyExtensions .HandleTransientHttpError() .Or<TimeoutRejectedException>() // 把Polly单次超时的异常纳入重试范围 .WaitAndRetryAsync( retryCount: numberOfAttempts, sleepDurationProvider: retryAttempt => ((timeOfAttempts == null) || (timeOfAttempts.Length != numberOfAttempts)) ? TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) : timeOfAttempts[retryAttempt], onRetry: (exception, retryCount, context) => { Logging.Global.LogError($"Retry {retryCount} of {context.PolicyKey} at {context.OperationKey}, due to: {exception}."); }); // 组合策略:每次重试都会先应用单次超时 return Policy.WrapAsync(retryPolicy, singleTimeoutPolicy); }
然后在Startup里替换原来的重试策略注册:
services.AddHttpClient<IMyClient, MyClient>() .AddHttpMessageHandler<LoggingDelegatingHandler>() // 先添加断路器(外层),再添加重试+超时(内层) .AddPolicyHandler(ResiliencyPolicy.GetCircuitBreakerPolicy()) .AddPolicyHandler(ResiliencyPolicy.GetCombinedRetryAndTimeoutPolicy(attempts, timeouts, TimeSpan.FromSeconds(15)));
方案2:调整HttpClient的全局超时
如果你不想引入单次超时策略,可以直接延长HttpClient的全局超时时间,确保它大于重试总等待时间+所有请求的预估耗时总和。比如:
services.AddHttpClient<IMyClient, MyClient>() .AddHttpMessageHandler<LoggingDelegatingHandler>() .AddPolicyHandler(ResiliencyPolicy.GetRetryPolicy(attempts, timeouts)) .AddPolicyHandler(ResiliencyPolicy.GetCircuitBreakerPolicy()) .ConfigureHttpClient(client => { client.Timeout = TimeSpan.FromSeconds(150); // 延长全局超时到150秒 });
⚠️ 注意:全局超时设置过长可能导致资源被长时间占用,要结合业务场景权衡。
方案3:优化重试策略的参数
减少重试次数,或者缩短重试等待时间,让总重试时长+请求耗时不超过100秒。比如把指数退避改成固定短间隔,或者减少重试次数到3次。
额外注意点
- Policy的注册顺序:HttpClient的PolicyHandler是倒序执行的,先添加的是外层策略,后添加的是内层策略。所以要确保断路器在最外层,重试+超时在内层,这样断路器会监控所有重试的结果。
- CancellationToken的配合:你的
Notify方法里用到了CancellationToken,要确保这个Token的超时时间和你的策略超时不冲突,避免提前取消请求。
内容的提问来源于stack exchange,提问作者Christy Pirumova

