Polly v8 实战:给 HTTP 请求穿上防弹衣

32 次阅读 0 点赞 0 评论 12 分钟原创技术教程

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

#Polly #.NET #C# #弹性架构 #微服务 #ASP.NET Core #重试策略 #熔断器
Polly v8 实战:给 HTTP 请求穿上防弹衣

Polly v8 实战:给 HTTP 请求穿上防弹衣

服务调用第三方支付接口时,对方偶尔超时或返回 503,你没有做任何重试,直接报错返回。用户一脸懵——明明就是一次正常的购买,怎么就失败了?

更危险的是,如果对端服务已开始出问题,你的服务还在不停地重试、堆积线程池,最终把自己也拖垮,这就是经典的「雪崩效应」。

这些问题都指向同一个需求:弹性(Resilience)。网络请求天生不可靠,超时、重试、熔断不是可选项,而是生产环境的必选项。今天我们用 .NET 生态最成熟的弹性处理库 Polly,一步步给服务加上「防弹衣」。

前置条件

  • 已安装 .NET 8 SDK(Polly v8 要求 .NET 8+)
  • 熟悉基本的 C# 异步编程(async/awaitCancellationToken
  • 了解 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 → 真正请求

熔断器在最外层判断是否放行,放行后进入重试层,重试层发起带超时的请求。这个顺序保证最合理的错误处理层级。

踩坑提醒

  1. CancellationToken 必须传递:Polly 超时策略依赖协作式取消,执行委托里忽略 token 会导致超时失效。始终把 token 传给所有异步方法。

  2. 不要对所有异常重试ArgumentExceptionArgumentNullException 这类编程错误重试无意义。只重试瞬时故障(网络超时、5xx、连接拒绝)。

  3. 熔断器需要足够流量MinimumThroughput 设太大小流量服务触发不了;设太小偶发错误可能误熔断。根据 QPS 调整。

  4. 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 生态最成熟的弹性处理库。

最后更新:2026-06-11T10:02:46

评论 (0)

发表评论

blog.comments.form.loading
0/500
加载评论中...