如何返回408请求超时给Polly以触发其重试策略?
解决方案
问题根源
服务器宕机时,客户端无法收到408响应,而是触发HttpClient超时抛出TaskCanceledException,而Polly的HandleTransientHttpError()默认不处理这类异常,导致重试策略不生效。另外,直接设置HttpClient.Timeout是全局超时(覆盖所有重试请求的总时长),并非单次请求超时,因此无法达到预期效果。
方法一:扩展Polly策略,直接处理超时异常
修改重试策略,添加对超时导致的TaskCanceledException的捕获,排除用户主动取消的情况:
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() // 仅处理超时引发的取消异常,排除主动取消操作 .Or<TaskCanceledException>(ex => !ex.CancellationToken.IsCancellationRequested) .WaitAndRetryAsync( retryCount: 3, sleepDurationProvider: currNthRetry => TimeSpan.FromSeconds(Math.Pow(2, currNthRetry)), onRetry: (outcome, retryCount, context) => { // 可在此添加日志记录,比如输出重试次数、异常信息 }); }
方法二:用Polly超时策略转化为408响应
使用Polly的TimeoutPolicy控制单次请求超时,并将超时转化为408状态码,让HandleTransientHttpError()自动识别触发重试:
services .AddHttpClient<IOrderService, OrderService>(c => { c.BaseAddress = new Uri(Configuration["ApiSettings:xxx"]); // 将HttpClient全局超时设为更长时间,交由Polly控制单次请求超时 c.Timeout = TimeSpan.FromMinutes(1); }) .AddPolicyHandler(GetCombinedPolicy()); private static IAsyncPolicy<HttpResponseMessage> GetCombinedPolicy() { // 单次请求超时策略,超时后返回408响应 var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(5), (context, timespan, task) => { return Task.FromResult(new HttpResponseMessage(HttpStatusCode.RequestTimeout)); }); var retryPolicy = HttpPolicyExtensions .HandleTransientHttpError() .WaitAndRetryAsync( retryCount: 3, sleepDurationProvider: currNthRetry => TimeSpan.FromSeconds(Math.Pow(2, currNthRetry)), onRetry: (outcome, retryCount, context) => { // 日志记录逻辑 }); // 组合策略:先执行单次超时控制,再执行重试 return Policy.WrapAsync(retryPolicy, timeoutPolicy); }
关键说明
HandleTransientHttpError()默认处理的范围包括:5xx状态码、408请求超时、HttpRequestException(如连接失败、DNS解析错误),但不包含TaskCanceledException。- 直接设置
HttpClient.Timeout是所有重试请求的总超时,而非单次请求超时,因此无法触发单次超时后的重试。使用Polly超时策略可以精准控制单次请求的超时时间,并将超时转化为标准408响应,适配现有重试逻辑。
内容的提问来源于stack exchange,提问作者user22155685
相关产品推荐
相关产品推荐

