Polly v8 实战:给 HTTP 请求穿上防弹衣
网络请求天生不可靠,超时、重试、熔断是生产环境必选项。本文带你用 Polly v8 为 .NET 服务配置 Retry + Circuit Breaker + Timeout 弹性策略组合,集成到 ASP.NET Core 依赖注入中,让服务不再因第三方接口波动而雪崩。

Polly v8 实战:给 HTTP 请求穿上防弹衣
服务调用第三方支付接口时,对方偶尔超时或返回 503,你没有做任何重试,直接报错返回。用户一脸懵——明明就是一次正常的购买,怎么就失败了?
更危险的是,如果对端服务已开始出问题,你的服务还在不停地重试、堆积线程池,最终把自己也拖垮,这就是经典的「雪崩效应」。
这些问题都指向同一个需求:弹性(Resilience)。网络请求天生不可靠,超时、重试、熔断不是可选项,而是生产环境的必选项。今天我们用 .NET 生态最成熟的弹性处理库 Polly,一步步给服务加上「防弹衣」。
前置条件
- 已安装 .NET 8 SDK(Polly v8 要求 .NET 8+)
- 熟悉基本的 C# 异步编程(
async/await、CancellationToken) - 了解
HttpClient的基础用法 - 一个简单的 ASP.NET Core 项目或控制台项目
安装 Polly 核心包
打开终端,进入项目目录执行:
bash
dotnet add package Polly.Core
dotnet add package Polly.Extensions
为什么用 Polly.Core 而不是老牌的 Polly 包?Polly 从 v8 开始做了重大重构,Polly.Core 是全新的、更轻量、性能更好的核心 API,也是官方推荐的主流用法。老包主要为兼容 v7 用户保留。
若要在 ASP.NET Core 里用依赖注入注册策略(生产环境常规做法),还需要 Polly.Extensions。
理解「弹性管道」核心概念
Polly v8 的设计哲学是 Pipeline(管道):把需要的策略像搭积木一样串起来,形成管道,让请求穿过它。管道的执行顺序是从外到内——先加的策略先拦截结果。
典型组合顺序:Timeout → Retry → Circuit Breaker
- Timeout 最内层:保证单个请求不会无限挂起
- Retry 中间层:超时或失败后重试,每次重试也带超时保护
- Circuit Breaker 最外层:重试无力回天时,直接熔断,不再浪费资源
快速体验 API 形态:
csharp
using Polly;
using Polly.Retry;
using Polly.Timeout;
ResiliencePipeline pipeline = new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions())
.AddTimeout(TimeSpan.FromSeconds(10))
.Build();
await pipeline.ExecuteAsync(async token =>
{
using var client = new HttpClient();
var response = await client.GetAsync("https://api.example.com/data", token);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync(token);
}, CancellationToken.None);
默认配置已具备基本弹性能力。下面逐一精确配置。
配置重试策略
重试不是无脑 while(true)。合理重试要考虑:次数、间隔、退避策略、哪些异常值得重试。
csharp
var retryOptions = new RetryStrategyOptions
{
ShouldHandle = new PredicateBuilder()
.Handle<HttpRequestException>()
.HandleResult<HttpResponseMessage>(r =>
(int)r.StatusCode >= 500),
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
MaxRetryAttempts = 3,
Delay = TimeSpan.FromSeconds(2),
OnRetry = static args =>
{
Console.WriteLine($"第 {args.AttemptNumber} 次重试,原因: {args.Outcome.Exception?.Message}");
return default;
}
};
指数退避 + Jitter 的必要性:所有客户端同时重试会对下游造成二次冲击。指数退避让间隔逐渐拉长(2s → 4s → 8s),Jitter 加入随机扰动错开重试时间点,这是工业级重试标配。
加入熔断器
熔断器的核心思路是:服务持续失败时,暂停调用让它歇一会。
csharp
var circuitBreakerOptions = new CircuitBreakerStrategyOptions<HttpResponseMessage>
{
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
.Handle<HttpRequestException>()
.HandleResult(r => (int)r.StatusCode >= 500),
FailureRatio = 0.5,
SamplingDuration = TimeSpan.FromSeconds(30),
MinimumThroughput = 8,
BreakDuration = TimeSpan.FromSeconds(60)
};
熔断器三状态:
- Closed(闭合):正常请求
- Open(断开):失败率超标,直接拒绝,快速失败
- Half-Open(半开):熔断到期后放少量请求试探,成功则恢复 Closed,失败回到 Open
完整实战:ASP.NET Core 注册弹性 HTTP 客户端
生产环境通常用依赖注入管理策略,方便测试和维护。
确保已安装所需包后,在 Program.cs 中注册:
csharp
using Polly;
using Polly.Retry;
using Polly.CircuitBreaker;
using Polly.Timeout;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient("resilient-client")
.AddResilienceHandler("my-pipeline", pipelineBuilder =>
{
pipelineBuilder
.AddTimeout(TimeSpan.FromSeconds(5))
.AddRetry(new RetryStrategyOptions<HttpResponseMessage>
{
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
.Handle<HttpRequestException>()
.HandleResult(r => (int)r.StatusCode >= 500),
BackoffType = DelayBackoffType.Exponential,
UseJitter = true,
MaxRetryAttempts = 3,
Delay = TimeSpan.FromSeconds(2),
})
.AddCircuitBreaker(new CircuitBreakerStrategyOptions<HttpResponseMessage>
{
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
.Handle<HttpRequestException>()
.HandleResult(r => (int)r.StatusCode >= 500),
FailureRatio = 0.5,
SamplingDuration = TimeSpan.FromSeconds(30),
MinimumThroughput = 8,
BreakDuration = TimeSpan.FromSeconds(60),
});
});
var app = builder.Build();
app.MapGet("/weather", async (IHttpClientFactory factory) =>
{
var client = factory.CreateClient("resilient-client");
var response = await client.GetAsync("https://api.open-meteo.com/v1/forecast");
response.EnsureSuccessStatusCode();
return Results.Ok(await response.Content.ReadAsStringAsync());
});
app.Run();
策略叠加顺序很关键:Polly 策略链从外到内执行。上面注册顺序是 Timeout → Retry → Circuit Breaker,实际请求流向是:
CircuitBreaker → Retry → Timeout → 真正请求
熔断器在最外层判断是否放行,放行后进入重试层,重试层发起带超时的请求。这个顺序保证最合理的错误处理层级。
踩坑提醒
-
CancellationToken 必须传递:Polly 超时策略依赖协作式取消,执行委托里忽略
token会导致超时失效。始终把token传给所有异步方法。 -
不要对所有异常重试:
ArgumentException、ArgumentNullException这类编程错误重试无意义。只重试瞬时故障(网络超时、5xx、连接拒绝)。 -
熔断器需要足够流量:
MinimumThroughput设太大小流量服务触发不了;设太小偶发错误可能误熔断。根据 QPS 调整。 -
Jitter 不是可选项:高并发场景下不加 Jitter 会导致「重试风暴」,把脆弱的下游直接打挂。
回顾与下一步
今天完成了这些事:
- 安装 Polly.Core 和 Polly.Extensions
- 理解 Pipeline 设计理念
- 配置指数退避 + 抖动的重试策略
- 配置基于失败率的熔断器
- 在 ASP.NET Core 中注册完整弹性 HttpClient
Polly 还有更多可探索的策略:Fallback(失败返回兜底值)、Hedging(并行发多个请求取最快)、Rate Limiter(限流)。若需要可观测性,Polly.Extensions 内置 OpenTelemetry 集成,可直接对接 Prometheus + Grafana。
把这套弹性思维用到下一个项目里,服务会感谢你。
**本文教程基于 Polly 项目,该库在 GitHub 上已获得 14186 Stars,是 .NET 生态最成熟的弹性处理库。