polly是c#最可靠灵活的重试方案,但http客户端需配合ihttpclientfactory和addpolicyhandler注册,纯业务逻辑用policy.handle().retryasync()即可;关键要区分异常类型、用waitandretryasync实现指数退避与jitter、避免手写死循环漏控点。

直接说结论:用 Polly 是目前 C# 最可靠、最灵活的自动重试方案,但别一上来就堆 Polly.Extensions.Http —— 纯业务逻辑重试,用 Policy.Handle<texception>().RetryAsync()</texception> 就够了;HTTP 客户端重试,才需要搭配 IHttpClientFactory 和 AddPolicyHandler。
什么时候该用 Polly 而不是手写 while + try/catch
手写重试容易漏掉关键控制点:比如重试间隔是否指数退避、是否区分异常类型、是否限制总耗时、是否支持异步取消。Polly 内置这些能力,且线程安全。常见踩坑是把 RetryAsync 用在同步方法里导致死锁,或没传 CancellationToken 导致无法中断长重试。
- 需要按异常类型差异化处理(如只重试
HttpRequestException,不重试ArgumentException)→ 必用Handle<t>()</t> - 要求第二次重试等待 2 秒、第三次 4 秒 → 用
WaitAndRetryAsync配合TimeSpan.FromSeconds(2)数组 - 重试过程中用户点了“取消” → 所有
ExecuteAsync方法都必须接收CancellationToken - 重试逻辑嵌在 ASP.NET Core 中间件或服务里 → 优先注册为单例
IRetryPolicy,避免每次新建策略开销
RetryAsync 和 WaitAndRetryAsync 的核心区别
RetryAsync 是固定延迟、固定次数;WaitAndRetryAsync 才能实现退避策略(如指数退避、抖动)。很多人误以为 RetryAsync(3) 会自动加延迟,其实它默认立即重试 —— 这会导致下游被瞬间打爆。
-
Policy.Handle<timeoutexception>().RetryAsync(3)</timeoutexception>:失败后立刻重试 3 次,无间隔 -
Policy.Handle<httprequestexception>().WaitAndRetryAsync(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(4) })</httprequestexception>:按指定间隔逐次重试 - 想加随机抖动防雪崩?用
WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)) + TimeSpan.FromMilliseconds(Random.Shared.Next(100)))
ASP.NET Core 中集成 HttpClient 重试的正确姿势
别在每个 HttpClient 实例上手动套 Policy —— 这样无法复用、无法集中管理,还可能引发 DNS 缓存问题。正确做法是通过 IHttpClientFactory 注册命名客户端,并用 AddPolicyHandler 绑定策略。
- 注册时指定策略名:
services.AddHttpClient("github", c => c.BaseAddress = new Uri("https://api.github.com/")) .AddPolicyHandler(GetRetryPolicy()); -
GetRetryPolicy()应返回IAsyncPolicy<httpresponsemessage></httpresponsemessage>,不是泛型Policy—— 否则编译报错Cannot convert lambda expression - 如果同时需要熔断(Circuit Breaker),用
Policy.WrapAsync(retryPolicy, circuitBreakerPolicy),顺序不能反:外层是重试,内层是熔断 - 调试时开启日志:在
OnRetry回调里写logger.LogWarning("Retry #{0} due to {1}", context.RetryCount, context.Exception?.GetType().Name)
容易被忽略的三个细节
一是 HttpClient 本身不参与重试 —— Polly 重试的是 SendAsync 调用,不是整个请求生命周期;二是 HttpRequestException 的 InnerException 可能是 SocketException 或 TaskCanceledException,需用 HandleInner<socketexception>()</socketexception> 显式捕获;三是重试策略若定义在静态字段里,多个并发请求共享同一策略实例是安全的,但别在策略里捕获并修改局部状态变量。










