重试策略需显式设次数、指数退避和总超时;熔断器须自定义shouldHandle识别业务错误;重试与熔断须用PolicyWrapAsync组合,外熔断内重试;HttpClient超时应设为MaxValue交由Polly控制。

重试策略怎么配才不卡死请求
默认的 RetryPolicy 不设超时或重试间隔,容易让请求卡住几秒甚至更久。必须显式控制重试次数、退避方式和总耗时上限。
- 用
WaitAndRetryAsync配合指数退避(如Backoff.DecorrelatedJitterBackoffV2),避免下游雪崩式重试 - 在
HttpClient外层加CancellationToken,并传给ExecuteAsync,防止策略内部无限等待 - 别只靠重试次数限制——加上
TimeSpan.FromMilliseconds(3000)这类硬性超时,否则网络卡顿时策略会等满所有重试周期 - 示例中常见错误:把
HttpRequestException和TaskCanceledException混在一起重试,其实后者大概率是上游已取消,再试没意义
熔断器触发后为什么还在发请求
CircuitBreakerPolicy 默认只对异常类型生效,HTTP 200 但业务返回 {"code":500} 这类“假成功”不会触发熔断,得手动判断响应内容。
- 用
AdvancedCircuitBreakerAsync,配合自定义shouldHandle函数,检查response.IsSuccessStatusCode == false或解析 JSON 后判断responseContent.code != 0 - 熔断状态不是全局共享的——每个
HttpClient实例或每个策略实例独立维护状态,别误以为配置一次就全应用生效 - 注意
failureThreshold是“失败比例”,不是绝对次数;比如设 0.5,意味着最近 10 次里有 5 次失败才会熔断,不是第 5 次就跳闸 - 熔断后调用会直接抛
BrokenCircuitException,不是静默失败,要确保上层有捕获逻辑,否则可能引发未处理异常崩溃
如何把重试 + 熔断串成一条链
不能简单 new 两个策略然后分别调用——必须用 PolicyWrapAsync 组合,否则熔断器收不到重试后的最终结果,无法准确统计失败率。
- 顺序很重要:外层放熔断(
circuitBreaker),内层放重试(retry),这样重试过程中的失败都算进熔断统计 - 别漏掉
Policy.WrapAsync(retry, circuitBreaker)的括号方向,写反会导致编译通过但运行时策略不生效 - 组合后调用
policyWrap.ExecuteAsync(() => httpClient.GetAsync(url)),而不是分别执行两个ExecuteAsync - 如果还加了降级(
FallbackPolicy),它应该放在最外层,兜住熔断和重试都失败的情况
HttpClient 与 Polly 怎么共存不冲突
直接给 HttpClient 设置 Timeout 会跟 Polly 的重试逻辑打架——比如 HttpClient 内部 10 秒超时,而 Polly 重试 3 次每次等 2 秒,第三次还没发出去就被 HttpClient 自己扔异常了。
- 把
HttpClient.Timeout设为TimeSpan.MaxValue,把超时控制权完全交给 Polly - 每个
HttpClient实例应绑定一个固定的策略实例(推荐用 DI 注入单例策略),不要每次请求都 new 新策略,否则熔断状态无法累积 - 如果用了
IHttpClientFactory,在AddHttpClient里用AddPolicyHandler注册策略,它会自动处理生命周期和策略复用 - 注意
HttpRequestException的 InnerException 可能是SocketException或OperationCanceledException,Polly 默认不展开 InnerException,需手动写ex.InnerException is TimeoutException这类判断










