一、安装依赖
# 基础包
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();
四、实践
- 重试:只用于瞬时、可自愈故障(网络、5xx);不用于业务错误(400/404);加抖动防重试风暴。
- 熔断:全局单例 Policy,避免多实例导致计数失效;阈值按业务调整(敏感易抖动,宽松易雪崩)。
- 降级:降级响应与原接口结构 / Content-Type 一致,避免上游解析异常;记录降级日志用于监控。
- 组合:熔断在外、重试在内,防止熔断打开后仍做无效重试。
- 监控:监听
onBreak/onReset/onFallback,接入 Prometheus/Grafana 告警。
来源:https://www.cnblogs.com/chuansheng/p/19916139 |