基于重试次数调整超时时长:Polly实现方案咨询
实现Polly动态超时+重试的方案
刚好碰到过类似的需求,咱们可以通过Polly的策略组合+上下文传递重试次数来实现首次短超时、重试时递增超时的效果,直接上代码和解释:
核心思路
我们需要把Retry策略和Timeout策略结合起来:
- 用Retry策略捕获超时(或其他需要重试的异常),并在每次重试时把当前重试次数存入Polly的
Context对象 - Timeout策略从
Context中读取重试次数,动态计算对应的超时时长 - 把两个策略组合,让每次重试都使用对应的超时时间
完整代码示例
using Polly; using Polly.Timeout; using System.Net.Http; // 定义超时时长的计算逻辑:首次请求短超时,重试时递增 TimeSpan CalculateTimeout(int retryCount) { // 示例规则:首次500ms,每次重试加1000ms;也可以改成指数退避(比如500→1000→2000→4000) return retryCount == 0 ? TimeSpan.FromMilliseconds(500) : TimeSpan.FromMilliseconds(500 + retryCount * 1000); } // 1. 创建Retry策略:捕获超时异常,重试时记录次数到Context var retryPolicy = Policy .Handle<TimeoutRejectedException>() // 捕获Timeout策略抛出的超时异常 .Or<HttpRequestException>() // 如果请求本身失败也需要重试,可以加上这个 .Retry(3, (exception, retryCount, context) => { // 把当前重试次数存入Context,供Timeout策略使用 context["RetryCount"] = retryCount; Console.WriteLine($"触发第 {retryCount} 次重试,原因:{exception.Message}"); }); // 2. 创建动态Timeout策略:从Context读取重试次数,计算超时 var timeoutPolicy = Policy.Timeout((context) => { // 首次请求时,Context里还没有RetryCount,默认用0(首次超时) if (context.TryGetValue("RetryCount", out var retryCountObj) && retryCountObj is int retryCount) { return CalculateTimeout(retryCount); } return CalculateTimeout(0); }); // 3. 组合策略:Retry在外,Timeout在内,确保每次重试都应用对应超时 var combinedPolicy = Policy.Wrap(retryPolicy, timeoutPolicy); // 4. 执行你的请求逻辑 try { var result = await combinedPolicy.ExecuteAsync(async (context) => { // 这里替换成你的实际请求代码,比如调用HttpClient using var client = new HttpClient(); var response = await client.GetAsync("https://your-api-endpoint.com"); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); }, new Context()); // 传递Context用于存储重试次数 Console.WriteLine("请求成功:" + result); } catch (TimeoutRejectedException) { Console.WriteLine("所有重试均超时,请求失败"); } catch (HttpRequestException) { Console.WriteLine("请求失败,重试后仍未恢复"); }
关键细节说明
- 重试次数对应关系:首次请求没有重试(
retryCount=0),第一次重试对应retryCount=1,以此类推,刚好匹配我们的超时计算逻辑 - 超时规则自定义:你可以随便修改
CalculateTimeout方法的逻辑,比如改成指数退避(500 * Math.Pow(2, retryCount)),或者固定的阶梯时长,完全看你的需求 - 异常范围控制:如果只需要在超时的时候重试,就只保留
.Handle<TimeoutRejectedException>();如果其他请求异常(比如网络错误)也需要重试,就加上.Or<HttpRequestException>()等你需要的异常类型 - 同步/异步兼容:如果是同步请求,把
ExecuteAsync改成Execute即可,其他逻辑完全一致
内容的提问来源于stack exchange,提问作者Danation
相关产品推荐
相关产品推荐

