polly重试策略仅在ihttpclientfactory注册的客户端上生效,需显式捕获503等非异常http状态码;应使用decorrelatedjitterbackoffv2避免雪崩;超时策略必须最外层包装,httpclient.timeout设为timespan.maxvalue;策略仅通过addpolicyhandler注册才生效。

直接说结论:Polly 重试策略不是“加了就自动重试”,它只在 IHttpClientFactory 注册的客户端上生效,且必须精准捕获异常或失败响应码——漏掉 HandleResult 就会跳过 503、404 这类不抛异常的 HTTP 错误。
为什么 HttpClient 返回 503 却不重试?
因为 HttpClient 对 503、404、400 等状态码默认不抛异常,而是正常返回 HttpResponseMessage。Polly 的 HandleTransientHttpError() 只覆盖 5xx、408、网络异常,不包含 503。
- 必须显式补上
OrResult(r => r.StatusCode == HttpStatusCode.ServiceUnavailable) - 如果还要重试 404,得额外加
OrResult(r => r.StatusCode == HttpStatusCode.NotFound) - 别只写
Handle<httprequestexception>()</httprequestexception>,那对 503 完全无效
WaitAndRetryAsync 怎么避免雪崩式重试?
用 Math.Pow(2, retryAttempt) 写指数退避看似合理,但所有实例会在同一时刻重试,容易压垮下游。真实场景要加抖动(jitter)。
- 改用
Backoff.DecorrelatedJitterBackoffV2(medianFirstRetryDelay: TimeSpan.FromSeconds(0.5), retryCount: 3) - 不要手写
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),它没随机性 - 退避基线别设太大——首重试延迟超过 1 秒,在高并发下可能引发连锁超时
重试 + 超时 + 熔断怎么串才不打架?
顺序错了,策略之间就会互相截断:比如超时设在内层,重试还没发完就被砍掉;熔断器收不到重试后的最终结果,failureThreshold 就是废的。
-
Timeout必须放在最外层,用Policy.WrapAsync(timeout, retry, circuitBreaker)显式声明顺序 -
HttpClient.Timeout必须设为TimeSpan.MaxValue,否则和 Polly 超时冲突 - 熔断器的
SamplingDuration和MinimumThroughput得配对用,例如SamplingDuration = TimeSpan.FromSeconds(30)且MinimumThroughput = 20,否则低流量下永远熔不断
最容易被忽略的是:所有策略只绑定在 AddPolicyHandler() 链路上。手动 new HttpClient()、或从 DI 容器 resolve 出未注册策略的 IHttpClientFactory 实例,Polly 根本不介入——连日志都不会打,静默失效。











