在微服务架构下,多个服务之间通过网络进行API调用是常见场景,一旦某个下游服务出现故障,大量请求持续涌入会导致故障范围扩大,甚至引发整个系统雪崩。Polly作为.NET平台成熟的故障处理库,提供了熔断器、重试、超时等多种容错策略,其中熔断器模式是应对服务故障的核心手段之一。

Polly熔断器核心概念
熔断器本质上是一个状态机,包含三种核心状态:
- 关闭状态:正常状态,请求可以正常发送到下游服务,同时统计失败请求的数量。
- 打开状态:当失败请求数量达到预设阈值时触发,此时所有请求会直接被拒绝,不再发送到下游服务,避免无效请求占用资源。
- 半开状态:熔断持续时间结束后,熔断器进入半开状态,允许少量请求尝试调用下游服务,如果请求成功则恢复到关闭状态,否则重新回到打开状态。
项目准备与依赖引入
首先需要在C#项目中安装Polly的NuGet包,可以通过包管理器控制台执行以下命令:
Install-Package Polly
如果是.NET Core或.NET 5及以上项目,也可以直接编辑项目文件添加依赖:
<PackageReference Include="Polly" Version="8.3.0" />
基础熔断器配置与API调用示例
下面以调用外部用户服务API为例,演示如何配置Polly熔断器实现容错:
using Polly;
using Polly.CircuitBreaker;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class UserServiceClient
{
private readonly HttpClient _httpClient;
// 定义熔断器策略
private readonly AsyncCircuitBreakerPolicy<HttpResponseMessage> _circuitBreakerPolicy;
public UserServiceClient(HttpClient httpClient)
{
_httpClient = httpClient;
// 配置熔断器:连续3次请求失败则触发熔断,熔断持续10秒
_circuitBreakerPolicy = Policy
.Handle<HttpRequestException>() // 处理网络请求异常
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode) // 处理非成功状态码的响应
.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 3, // 触发熔断的失败次数阈值
durationOfBreak: TimeSpan.FromSeconds(10), // 熔断持续时间
onBreak: (result, breakDelay, context) => // 熔断触发时的回调
{
Console.WriteLine($"熔断器触发,进入打开状态,持续{breakDelay.TotalSeconds}秒");
},
onReset: (context) => // 熔断恢复时的回调
{
Console.WriteLine("熔断器恢复,进入关闭状态");
},
onHalfOpen: () => // 进入半开状态时的回调
{
Console.WriteLine("熔断器进入半开状态,尝试少量请求");
}
);
}
public async Task<string> GetUserNameAsync(int userId)
{
try
{
// 使用熔断器策略执行API调用
var response = await _circuitBreakerPolicy.ExecuteAsync(async () =>
{
return await _httpClient.GetAsync($"https://ipipp.com/api/users/{userId}");
});
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return "获取用户信息失败";
}
catch (BrokenCircuitException)
{
// 捕获熔断器打开时抛出的异常
return "服务暂时不可用,请稍后重试";
}
}
}
熔断器参数调优建议
实际使用中需要根据业务场景调整熔断器的配置参数,以下是常见参数的调优方向:
| 参数 | 说明 | 调优建议 |
|---|---|---|
| handledEventsAllowedBeforeBreaking | 触发熔断的失败次数阈值 | 调用耗时短的服务可设置3-5次,耗时长的服务可适当提高至5-10次 |
| durationOfBreak | 熔断持续时间 | 下游服务恢复耗时短可设置5-10秒,恢复慢的服务可设置30秒以上 |
| 采样周期 | 统计失败请求的时间范围 | 默认是统计所有请求,可结合高级配置设置滑动窗口统计,避免偶发故障误触发熔断 |
结合其他容错策略使用
Polly支持多种策略组合使用,通常熔断器会和重试策略配合使用,先通过重试处理偶发的网络波动,多次重试失败后再触发熔断,避免不必要的熔断:
using Polly;
using Polly.CircuitBreaker;
using Polly.Retry;
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class OptimizedUserServiceClient
{
private readonly HttpClient _httpClient;
private readonly AsyncPolicy<HttpResponseMessage> _combinedPolicy;
public OptimizedUserServiceClient(HttpClient httpClient)
{
_httpClient = httpClient;
// 定义重试策略:最多重试2次,每次重试间隔1秒
var retryPolicy = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(
retryCount: 2,
sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(1),
onRetry: (result, timeSpan, retryCount, context) =>
{
Console.WriteLine($"第{retryCount}次重试,间隔{timeSpan.TotalSeconds}秒");
}
);
// 定义熔断器策略
var circuitBreakerPolicy = Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.CircuitBreakerAsync(
handledEventsAllowedBeforeBreaking: 3,
durationOfBreak: TimeSpan.FromSeconds(10)
);
// 组合策略:先重试,再熔断
_combinedPolicy = Policy.WrapAsync(circuitBreakerPolicy, retryPolicy);
}
public async Task<string> GetUserNameAsync(int userId)
{
try
{
var response = await _combinedPolicy.ExecuteAsync(async () =>
{
return await _httpClient.GetAsync($"https://ipipp.com/api/users/{userId}");
});
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsStringAsync();
}
return "获取用户信息失败";
}
catch (BrokenCircuitException)
{
return "服务暂时不可用,请稍后重试";
}
}
}
注意事项
使用Polly熔断器时需要注意以下几点:
- 熔断器的状态是单例的,如果是多实例部署的服务,每个实例的熔断器状态相互独立,不会共享熔断状态。
- 不要在熔断器的处理逻辑中执行耗时过长的操作,避免影响策略的执行效率。
- 对于核心业务接口,建议结合监控告警,当熔断器触发时及时通知运维人员排查下游服务故障。