Polly 策略管道全解析:重试、熔断、超时,.NET 弹性治理一文搞定

Polly 策略管道全解析:重试、熔断、超时,.NET 弹性治理一文搞定 服务调用超时、下游偶发故障、瞬间流量冲垮依赖——这些场景你大概率都遇到过。手写重试循环 try-catch 堆栈代码又臭又长还经常漏掉边界比如退避时间、熔断保护。Polly 就是专门解决这些问题的库把“重试、熔断、超时、舱壁、回退”变成声明式策略。你只需要告诉它“遇到什么异常、怎么办”剩下的交给策略管道。本文不讲空话直接上 .NET 6 可运行的硬核示例。一、Polly 是什么Polly 是一个 .NET 弹性和瞬态故障处理库由 .NET 基金会支持。官方定义让你以流畅、线程安全的方式表达重试、熔断器、超时、舱壁隔离、回退等策略。它解决的核心问题是当调用外部服务HTTP、数据库、消息队列等出现瞬时故障时如何优雅地自愈而不是直接抛异常导致雪崩。官网Meet Polly: The .NET resilience library | Pollyhttps://www.pollydocs.org/GitHubGitHub - App-vNext/Polly: Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner. From version 6.0.1, Polly targets .NET Standard 1.1 and 2.0. · GitHubPolly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner. From version 6.0.1, Polly targets .NET Standard 1.1 and 2.0. - App-vNext/Pollyhttps://github.com/App-vNext/PollyPolly 能做什么5 个最常用作用自动重试解决瞬时故障遇到网络抖动、数据库超时等临时问题按指数退避或固定间隔自动重试避免手动写循环。例如请求第三方 API 时偶发 503重试 3 次后成功率从 90% 提升到 99.9%。熔断保护防止雪崩当下游连续失败 N 次熔断器打开后续请求直接失败快速失败不再压垮已脆弱的服务。一段时间后尝试放行少量请求探测下游是否恢复。超时控制避免线程阻塞给每个操作设定最大执行时间超时即抛出TimeoutRejectedException防止慢调用占满线程池。舱壁隔离限制并发为不同服务或关键路径设置最大并发数避免一个慢服务耗尽所有线程资源影响其他正常请求。降级回退提供备用结果当请求失败超时、异常、熔断时返回缓存数据、默认值或调用备用逻辑保证核心功能不中断。这 5 种能力可以单独使用也可以组合成弹性管道。下面我们就用代码逐一展示。二、怎么引入安装核心包及常用扩展HTTP 工厂集成dotnet add package Polly dotnet add package Polly.Extensions.Http dotnet add package Microsoft.Extensions.Http.Polly以上包均支持 .NET 6具体版本请以 NuGet.org 实际列表为准本文不硬写版本号。最小接入配置Program.csusing Polly; using Polly.Extensions.Http; var builder WebApplication.CreateBuilder(args); // 必须项注册 HttpClient 并附加 Polly 策略 builder.Services.AddHttpClient(catalog) .AddPolicyHandler(GetRetryPolicy()); static IAsyncPolicyHttpResponseMessage GetRetryPolicy() { return HttpPolicyExtensions .HandleTransientHttpError() // 处理网络错误或 HTTP 5xx/408 .WaitAndRetryAsync(3, retryAttempt TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); // 指数退避 } var app builder.Build(); // 可选项从容器中获取 IHttpClientFactory 并发送请求验证 app.MapGet(/test, async (IHttpClientFactory factory) { var client factory.CreateClient(catalog); var response await client.GetAsync(https://api.example/health); return response.IsSuccessStatusCode ? OK : Failed; }); app.Run();验证接入是否成功调用上述/test接口如果目标服务返回 5xx你能在日志或调试中观察到 Polly 自动重试 3 次最终才返回失败。没有手动 try-catch 重试逻辑即证明 Polly 已生效。三、快速上手前提创建一个 .NET 6 控制台或 Web 项目安装上述 NuGet 包。示例1最小可用重试同步using Polly; using Polly.Retry; int callCount 0; RetryPolicyint policy Policyint .HandleException() .Retry(3, (ex, retryCount) { Console.WriteLine($重试 {retryCount} 次原因: {ex.Message}); }); var result policy.Execute(() { callCount; Console.WriteLine($第 {callCount} 次执行); if (callCount 2) throw new InvalidOperationException(模拟失败); return 42; }); Console.WriteLine($最终结果: {result}); // 预期输出第1次失败重试1次第2次成功输出42示例2带超时和回退的异步策略using Polly; using Polly.Timeout; using Polly.Fallback; // 使用悲观超时策略不依赖 CancellationToken 的主动配合 var timeout Policy.TimeoutAsync(2, TimeoutStrategy.Pessimistic); var fallback Policystring .HandleTimeoutRejectedException() .FallbackAsync(【超时默认值】); var wrap fallback.WrapAsync(timeout); async Taskstring SlowApi() { await Task.Delay(3000); // 悲观超时会强制取消无需传递 ct return 正常数据; } var result await wrap.ExecuteAsync(async ct await SlowApi()); Console.WriteLine(result); // 输出 【超时默认值】说明TimeoutStrategy.Pessimistic会在超时后主动抛出TimeoutRejectedException不要求内部操作必须配合CancellationToken。若使用Optimistic模式则需要将CancellationToken传递给异步方法并内部响应取消。示例3熔断器 重试组合典型微服务场景using Polly; using Polly.CircuitBreaker; using Polly.Retry; // 重试策略每次执行失败后重试最多 2 次共执行 3 次尝试 var retry Policy .HandleHttpRequestException() .RetryAsync(2, onRetry: (ex, i) Console.WriteLine($重试 {i} 次原因: {ex.Message})); // 熔断器连续失败 3 次注意这里的失败是指经过重试后依然失败的完整执行 // 如果每次完整执行含内部重试都失败熔断器计数一次。 var breaker Policy .HandleHttpRequestException() .CircuitBreakerAsync(3, TimeSpan.FromSeconds(5), onBreak: (ex, span) Console.WriteLine($熔断开启持续 {span.Seconds}s), onReset: () Console.WriteLine(熔断关闭恢复请求)); // 组合外层重试内层熔断重试耗尽后的异常才会被熔断器捕获 var combined retry.WrapAsync(breaker); int requestId 0; while (requestId 10) { try { await combined.ExecuteAsync(async () { Console.WriteLine($发起请求 {requestId}); throw new HttpRequestException(服务不可用); }); } catch (BrokenCircuitException) { Console.WriteLine(熔断器开启快速失败); await Task.Delay(1000); } } // 预期行为每次完整请求含重试都失败连续 3 次完整请求失败后熔断器打开 // 后续请求快速失败直至 5 秒后进入半开状态。示例4在 ASP.NET Core 中使用策略注册推荐做法// Program.cs builder.Services.AddHttpClient(payment) .AddPolicyHandler((sp, _) { var retryCount 3; return HttpPolicyExtensions .HandleTransientHttpError() .WaitAndRetryAsync(retryCount, retryAttempt TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), onRetry: (outcome, timespan, retryAttempt, context) { var logger sp.GetRequiredServiceILoggerProgram(); logger.LogWarning(重试 {RetryAttempt} 次延迟 {Delay}ms, retryAttempt, timespan.TotalMilliseconds); }); });常见报错与修复报错信息触发条件修复方式TimeoutRejectedException超时策略触发操作未在指定时间内完成调大超时时间或优化下游性能若使用Optimistic需确保传递CancellationTokenBrokenCircuitException熔断器处于 Open 状态快速失败检查下游健康度等待恢复时间可考虑半开状态探测逻辑PolicyExecutionException策略内部异常如重试耗尽后仍失败检查内部委托是否正确或添加回退策略兜底四、适用场景适合用的场景高并发微服务网关调用多个下游用熔断器防止单点故障雪崩用舱壁隔离限制并发数。分布式系统网络抖动、瞬时超时普遍存在重试 指数退避是标准解法。单体应用调用外部 API对接第三方服务支付、短信、地图用超时重试提升成功率。事件处理/消息消费消费 Kafka/RabbitMQ 时如果处理失败用 Polly 实现退避重试避免频繁 Requeue。需要注意的边界写操作如转账、订单创建使用重试时必须幂等否则可能重复提交。建议搭配幂等键。熔断开启期间会直接失败适用于“快速失败比缓慢等待更好”的场景。如果业务不允许任何失败熔断可能不太适合。超时策略不宜设置过短误伤正常慢请求或过长积累阻塞。五、实战建议3个高频坑位坑位1重试策略未考虑幂等性导致数据重复触发条件对非幂等的 POST 请求使用自动重试下游可能收到多次请求。错误表现订单重复创建、余额重复扣减。修复策略仅对 GET 或幂等的 PUT/DELETE 使用重试写操作采用唯一请求 ID 下游去重。坑位2熔断器与重试顺序错误触发条件把重试放在熔断外面正常但还是组合错误导致重试耗尽后熔断不生效。错误表现熔断器永远不会开启因为重试内部已经把异常吞掉了。修复策略顺序应为重试.WrapAsync(熔断)即外层重试捕获异常后重试若重试耗尽则异常冒泡给熔断器再由熔断器统计失败次数。反之会导致熔断器看不到异常。坑位3在同步代码中混用异步策略导致死锁触发条件在 ASP.NET 非异步上下文或Task.Result上调用ExecuteAsync。错误表现死锁程序卡死。修复策略全链路使用异步 (ExecuteAsyncawait)如果必须同步用Task.Run包装或使用同步策略Policy.Execute。六、选型建议什么场景更适合 Polly已有 .NET 6 项目需要为 HTTP 调用增加基本容错能力。微服务数量不多20团队希望以声明式配置替代手写重试。需要标准化熔断、超时等策略避免各写各的。什么场景不太适合极端低延迟要求5ms且重试会显著增加延迟——可考虑只做超时不做重试。团队规模极小1-2人业务逻辑简单基本没有外部调用——引入 Polly 增加学习成本收益较低。已经使用云原生服务网格如 Istio做流量治理——Polly 可与网格互补但会增加重复配置。参考建议单体应用 少数外部 API仅使用超时 简单重试2次。中小微服务集群3-10个服务重试 熔断 舱壁隔离结合 Polly.Extensions.Http 统一配置。高并发大团队除了 Polly 客户端策略还应配合分布式追踪和降级网关。七、总结收尾Polly 把零散的错误处理逻辑变成了可组合、可测试的策略管道。回到开头的问题——你不需要再写for循环、手动 sleep、统计失败次数。一行.AddPolicyHandler()就能让 HttpClient 自动具备生产级韧性。可直接执行的落地建议在现有 .NET 6 项目的Program.cs里为关键 HttpClient 添加一个至少包含HandleTransientHttpErrorWaitAndRetryAsync(3, retryAttempt TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))的指数退避重试策略第1次重试延迟2秒第2次4秒第3次8秒并观察日志里是否出现重试记录。三分钟就能看到效果。