Polly重试策略生效需满足三个条件:精准捕获异常、退避带jitter、通过AddPolicyHandler注入DI管理的HttpClient;否则不触发。

直接说结论:Polly 重试策略不是“加了就生效”,必须满足三个硬性条件——异常捕获精准、退避带 jitter、策略通过 AddPolicyHandler 注入 DI 管理的 HttpClient;否则重试逻辑根本不会触发。
Handle() 捕获不到 HTTP 503 怎么办
HTTP 503 是成功响应(状态码 200–599 范围内),HttpRequestException 只在连接失败、DNS 解析失败、超时等底层网络异常时抛出,503 属于业务层失败,得用 HandleResult 显式判断。
- 正确写法:
HttpPolicyExtensions.HandleTransientHttpError()(它已内置覆盖 503、500、408、429 等) - 或手动指定:
HandleResult<httpresponsemessage>(r => !r.IsSuccessStatusCode)</httpresponsemessage> - 如果服务返回 200 但 JSON body 含
{"code":500},需配合AdvancedCircuitBreakerAsync+ 自定义shouldHandle函数解析响应体 - 别混用
Handle<httprequestexception>()</httprequestexception>和HandleResult在同一个策略里——前者不处理 503,后者不处理 DNS 失败,该拆就拆,别强塞
WaitAndRetryAsync 为什么重试间隔越来越长还雪崩
纯指数退避(如 TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))在高并发下会形成请求峰谷同步,下游扛不住。关键不是“要不要指数”,而是“有没有 jitter”。
- 禁用固定/线性退避:
WaitAndRetryAsync(3, i => TimeSpan.FromMilliseconds(100))—— 容易压垮下游 - 禁用无抖动指数:
i => TimeSpan.FromSeconds(Math.Pow(2, i))—— 多个请求同时重试第 2 次,全卡在 4 秒点 - 生产必用:
Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(0.5), retryCount: 3)—— 每次延迟随机扰动,天然错峰 - 注意:
Backoff.DecorrelatedJitterBackoffV2是Polly.Extensions.Http提供的,不是Polly核心包自带,漏装会编译报错
Retry + CircuitBreaker 组合后熔断器不触发
熔断器收不到失败信号,大概率是策略没串对顺序,或者没用 Policy.WrapAsync 包裹,而是分别调用了两个 ExecuteAsync。
- 错误姿势:
await retryPolicy.ExecuteAsync(...); await circuitBreaker.ExecuteAsync(...);—— 熔断器只看到最后一次调用,统计失真 - 正确嵌套:
Policy.WrapAsync(circuitBreaker, retryPolicy)—— 外层熔断、内层重试,所有重试过程中的失败都计入熔断统计 - failureThreshold 是比例,不是次数:设
0.5表示“最近采样窗口内失败率 ≥50%”,必须搭配SamplingDuration和MinimumThroughput,例如SamplingDuration = TimeSpan.FromSeconds(30), MinimumThroughput = 20 - 每个
HttpClient实例独立维护熔断状态,别指望一个命名客户端的熔断能影响另一个
AddPolicyHandler 不生效的典型原因
策略注册了,日志也打了,但请求失败就是不重试——90% 是因为没走对注册路径。
- 必须用
services.AddHttpClient("MyClient")注册命名客户端,再链式调用AddPolicyHandler(...);直接new HttpClient()或services.AddSingleton<httpclient>()</httpclient>完全无效 - 策略函数必须返回
IAsyncPolicy<httpresponsemessage></httpresponsemessage>,别返回IAsyncPolicy(泛型未指定)或Policy(缺少异步支持) - 别在控制器里手动
await client.GetAsync(...)后再套policy.ExecuteAsync—— 这样绕过了PolicyHttpMessageHandler,策略不介入请求生命周期 - 检查是否装了
Polly.Extensions.Http:它提供HttpPolicyExtensions和适配HttpClient的封装,缺了它,HandleTransientHttpError()等便捷方法不可用
最容易被忽略的一点:所有策略组合最终都依赖 PolicyHttpMessageHandler 插入到 HttpClient 的发送管道中,而这个 handler 只在 DI 创建客户端时自动注入。一旦你从 DI 容器外拿到 HttpClient 实例,或者用 GetService<httpclient>()</httpclient> 绕过命名注册,策略就彻底失效——这不是 bug,是设计使然。


















