.NET Polly 重试、熔断、降级

一、安装依赖

 

# 基础包
Install-Package Polly
# HTTP 集成(推荐) Install-Package Microsoft.Extensions.Http.Polly 

 


 

二、策略与代码

 

1. 重试(Retry):自愈瞬时故障

  作用:网络抖动、偶发超时、5xx 等临时错误自动重试,避免单次失败影响业务。   常用模式:固定次数、指数退避(重试间隔越来越长)、带抖动防风暴(前两者基础上加随机时间)。  

示例:HTTP 请求指数退避重试(3 次)

 

using Polly;
using Polly.Retry;
using System.Net.Http;

// 1. 定义重试策略(异步)
var retryPolicy = Policy<HttpResponseMessage>
    .Handle<HttpRequestException>()          // 捕获网络异常
    .OrResult(r => !r.IsSuccessStatusCode)  // 捕获非成功状态码
    .WaitAndRetryAsync(
        retryCount: 3,                      // 最多重试3次
        sleepDurationProvider: attempt =>   // 指数退避:1s→2s→4s + 随机抖动
            TimeSpan.FromSeconds(Math.Pow(2, attempt)) 
            + TimeSpan.FromMilliseconds(new Random().Next(100, 300)),
        onRetryAsync: (ex, span, attempt, _) => 
        {
            Console.WriteLine($"第{attempt}次重试,等待{span.TotalSeconds:F1}s,异常:{ex?.Message}");
            return Task.CompletedTask;
        }
    );

// 2. 执行受保护的调用
var httpClient = new HttpClient();
var response = await retryPolicy.ExecuteAsync(async () =>
{
    var res = await httpClient.GetAsync("https://api.example.com/data");
    res.EnsureSuccessStatusCode();
    return res;
});

 


 

2. 熔断(Circuit Breaker):防雪崩

  作用:连续失败达阈值后,主动切断请求(快速失败),熔断期内不发起真实调用;到期进入半开试探,成功则恢复。   状态:Closed(正常)→ Open(熔断)→ Half-Open(试探)→ Closed/Open。  

示例:连续 5 次失败熔断 10 秒

 

using Polly.CircuitBreaker;

// 定义熔断策略
var circuitPolicy = Policy<HttpResponseMessage>
    .Handle<HttpRequestException>()
    .OrResult(r => r.StatusCode == HttpStatusCode.InternalServerError)
    .CircuitBreakerAsync(
        exceptionsAllowedBeforeBreaking: 5,  // 连续5次失败触发熔断
        durationOfBreak: TimeSpan.FromSeconds(10), // 熔断10秒
        onBreak: (ex, span) => Console.WriteLine($"【熔断】断开{span.TotalSeconds}s,异常:{ex.Message}"),
        onReset: () => Console.WriteLine("【熔断】恢复正常"),
        onHalfOpen: () => Console.WriteLine("【熔断】半开,试探请求1次,成功则恢复正常,失败则继续熔断10秒")
    );

// 执行
try
{
    var response = await circuitPolicy.ExecuteAsync(async () =>
    {
        var res = await httpClient.GetAsync("https://api.example.com/data");
        res.EnsureSuccessStatusCode();
        return res;
    });
}
catch (BrokenCircuitException)
{
    Console.WriteLine("熔断器已打开,拒绝请求");
}

 


 

3. 降级(Fallback):兜底响应

  作用:所有重试 / 熔断都失败后,返回预设降级结果(默认值、缓存、简化数据),保证流程不中断。  

示例:HTTP 失败返回空数据降级

 

using Polly.Fallback;

// 定义降级策略
var fallbackPolicy = Policy<HttpResponseMessage>
    .Handle<Exception>()
    .OrResult(r => !r.IsSuccessStatusCode)
    .FallbackAsync(
        fallbackAction: _ => 
        {
            // 降级响应:与原接口同结构、同 Content-Type
            var fallback = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(
                    "{\"code\":0,\"data\":null,\"msg\":\"服务降级,稍后重试\"}",
                    Encoding.UTF8,
                    "application/json"
                )
            };
            return Task.FromResult(fallback);
        },
        onFallbackAsync: (ex, _) =>
        {
            Console.WriteLine($"【降级】触发,异常:{ex?.Message}");
            return Task.CompletedTask;
        }
    );

   


 

三、策略组合

 

1. 组合顺序

  外层:熔断 → 中层:重试 → 内层:降级   熔断控制整体开关,重试处理瞬时故障,降级兜底所有失败。  

示例:熔断 + 重试 + 降级组合(PolicyWrap)

 

// 组合策略(Wrap)
var resilientPolicy = Policy.WrapAsync(
    circuitPolicy,   // 外层:熔断(先判断是否熔断)
    retryPolicy,     // 中层:重试(熔断关闭时才重试)
    fallbackPolicy   // 内层:降级(都失败则兜底)
);

// 最终调用
var finalResponse = await resilientPolicy.ExecuteAsync(async () =>
{
    var res = await httpClient.GetAsync("https://api.example.com/data");
    res.EnsureSuccessStatusCode();
    return res;
});

 

2. ASP.NET Core + HttpClientFactory 集成

 

// Program.cs
var builder = WebApplication.CreateBuilder();

// 1. 定义策略
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}

static IAsyncPolicy<HttpResponseMessage> GetCircuitPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .CircuitBreakerAsync(5, TimeSpan.FromSeconds(10));
}

static IAsyncPolicy<HttpResponseMessage> GetFallbackPolicy()
{
    return Policy<HttpResponseMessage>
        .Handle<Exception>()
        .OrResult(r => !r.IsSuccessStatusCode)
        .FallbackAsync(_ => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent("{\"data\":null}", Encoding.UTF8, "application/json")
        }));
}

// 2. 注册带策略的 HttpClient
builder.Services.AddHttpClient("ResilientClient")
    .AddPolicyHandler(GetCircuitPolicy())
    .AddPolicyHandler(GetRetryPolicy())
    .AddPolicyHandler(GetFallbackPolicy());

var app = builder.Build();
app.Run();

 


 

四、实践

 

  1. 重试:只用于瞬时、可自愈故障(网络、5xx);不用于业务错误(400/404);加抖动防重试风暴。
  2. 熔断:全局单例 Policy,避免多实例导致计数失效;阈值按业务调整(敏感易抖动,宽松易雪崩)。
  3. 降级:降级响应与原接口结构 / Content-Type 一致,避免上游解析异常;记录降级日志用于监控。
  4. 组合:熔断在外、重试在内,防止熔断打开后仍做无效重试。
  5. 监控:监听 onBreak/onReset/onFallback,接入 Prometheus/Grafana 告警。

文章摘自:https://www.cnblogs.com/chuansheng/p/19916139