说明:
1、Polly官网:http://www.thepollyproject.org/
2、Polly安装:Nuget Microsoft.Extensions.Http.Polly
熔断:熔断开启之后,在熔断过程中将不再发送http请求,而是直接抛异常出来。等到熔断被自动关闭后再正常请求。
降级:熔断开启之后会抛出异常,降级机制则会捕获异常,然后可以进行自定义处理异常。
超时:Http请求的超时时间。
重试:Http请求失败后的重试,可以重试N次。
1、在Startup.cs类中 ConfigureServices方法配置
//断路器
services.AddHttpClient("small")
//降级
.AddPolicyHandler(Policy.HandleInner().FallbackAsync(fallbackResponse, async b =>
{
// 1、降级打印异常
Console.WriteLine($"服务开始降级,异常消息:{b.Exception.Message}");
// 2、降级后的数据
//Console.WriteLine($"服务降级内容响应:{fallbackResponse.Content.ToString()}");
await Task.CompletedTask;
}))
//熔断
.AddPolicyHandler(
Policy // HttpResponseMessage 为HttpClient的返回值
.Handle() //捕获Exception异常
.CircuitBreakerAsync(
8, // 出现3次异常
TimeSpan.FromSeconds(20), // 断路器的时间(例如:设置为2秒,短路器两秒后自动由开启到关闭)
(ex, ts) =>
{//熔断器开启事件触发
Console.WriteLine($"服务断路器开启,异常消息:{ex.Exception.Message}");
Console.WriteLine($"服务断路器开启的时间:{ts.TotalSeconds}s");
}, //断路器重置事件触发
() => {Console.WriteLine($"服务断路器重置");}, //断路器半开启事件触发
() => {Console.WriteLine($"服务断路器半开启(一会开,一会关)");}
)
)
//失败重试
.AddPolicyHandler(Policy.Handle().RetryAsync(3))//失败重试三次
//超时
.AddPolicyHandler(Policy.TimeoutAsync(TimeSpan.FromSeconds(2)));//设置2秒超时时间
2、使用HttpClient来发起请求
private readonly IHttpClientFactory _httpClientFactory;
public TeamService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task GetTeamInfos()
{
for (int i = 0; i < 15; i++)
{
try
{
Thread.Sleep(1000);
using HttpClient httpClient = _httpClientFactory.CreateClient("small");
//访问一个不存在的地址,这个时候会抛异常出来,会触发熔断机制
HttpResponseMessage requestResult = await httpClient.GetAsync($"https://localhost:5001/api/test");
}
catch (Exception ex)
{
Console.WriteLine("异常信息:" + ex.Message);
}
}
return null;
}
二、封装使用
2.1 新建配置文件:pollyconfig.json
{
"Polly": [
{
"ServiceName": [ "Serivce_Team", "Serivce_Member" ], //服务名称,可以多个服务使用同一个配置
"TimeoutTime": 5, //超时时间设置,单位为秒
"RetryCount": 2, //失败重试次数
"CircuitBreakerOpenFallCount": 2, //执行多少次异常,开启短路器(例:失败2次,开启断路器)
"CircuitBreakerDownTime": 6, //断路器关闭的时间(例如:设置为2秒,短路器两秒后自动由开启到关闭)
"HttpResponseMessage": "系统繁忙,请稍后再试!", //降级处理提示信息
"HttpResponseStatus": 200 //降级处理响应状态码
},
{
"ServiceName": [ "Serivce_Team" ], //假如服务名称存在相同的,则后面的会替换掉前面的
"TimeoutTime": 2,
"RetryCount": 5,
"CircuitBreakerOpenFallCount": 2,
"CircuitBreakerDownTime": 8,
"HttpResponseMessage": "系统繁忙,请稍后再试~!",
"HttpResponseStatus": 503
}
]
}
2.2 创建配置实体类:PollyHttpClientConfig.cs
public class PollyHttpClientConfig
{
///
/// 服务名称
///
public List ServiceName { set; get; }
///
/// 超时时间设置,单位为秒
///
public int TimeoutTime { set; get; }
///
/// 失败重试次数
///
public int RetryCount { set; get; }
///
/// 执行多少次异常,开启短路器(例:失败2次,开启断路器)
///
public int CircuitBreakerOpenFallCount { set; get; }
///
/// 断路器关闭的时间(例如:设置为2秒,短路器两秒后自动由开启到关闭)
///
public int CircuitBreakerDownTime { set; get; }
///
/// 降级处理消息(将异常消息封装成为正常消息返回,然后进行响应处理,例如:系统正在繁忙,请稍后处理.....)
///
public string HttpResponseMessage { set; get; }
///
/// 降级处理状态码(将异常消息封装成为正常消息返回,然后进行响应处理,例如:系统正在繁忙,请稍后处理.....)
///
public int HttpResponseStatus { set; get; }
}
2.3 封装拓展类:PollyHttpClientServiceCollectionExtension.cs
public static class PollyHttpClientServiceCollectionExtension
{
public static void AddPollyHttpClient(this IServiceCollection service)
{
//读取服务配置文件
try
{
var config = new ConfigurationBuilder().AddJsonFile("pollyconfig.json").Build(); //nuget: Microsoft.Extensions.Configuration.Json
List configList = config.GetSection("Polly").Get>(); // nuget: Microsoft.Extensions.Options.ConfigurationExtensions
if (configList != null && configList.Count > 0)
{
configList.ForEach((pollyHttpClientConfig) =>
{
service.AddPollyHttpClient(pollyHttpClientConfig);
});
}
}
catch (Exception ex)
{
throw new Exception("请正确配置pollyconfig.json");
}
}
public static void AddPollyHttpClient(this IServiceCollection service, PollyHttpClientConfig pollyHttpClientConfig)
{
if (pollyHttpClientConfig == null)
throw new Exception("请配置:pollyHttpClientConfig");
if (pollyHttpClientConfig.ServiceName == null || pollyHttpClientConfig.ServiceName.Count < 1)
throw new Exception("请配置:pollyHttpClientConfig.Polly.ServiceName");
for (int i = 0; i < pollyHttpClientConfig.ServiceName.Count; i++)
{
var builder = service.AddHttpClient(pollyHttpClientConfig.ServiceName[i]);
builder.BuildFallbackAsync(pollyHttpClientConfig.HttpResponseMessage, pollyHttpClientConfig.HttpResponseStatus);
builder.BuildCircuitBreakerAsync(pollyHttpClientConfig.CircuitBreakerOpenFallCount, pollyHttpClientConfig.CircuitBreakerDownTime);
builder.BuildRetryAsync(pollyHttpClientConfig.RetryCount);
builder.BuildTimeoutAsync(pollyHttpClientConfig.TimeoutTime);
}
}
//降级
private static void BuildFallbackAsync(this IHttpClientBuilder builder, string httpResponseMessage, int httpResponseStatus)
{
if (httpResponseStatus < 1 || string.IsNullOrEmpty(httpResponseMessage))
return;
HttpResponseMessage fallbackResponse = new HttpResponseMessage
{
Content = new StringContent(httpResponseMessage),
StatusCode = (HttpStatusCode)httpResponseStatus
};
builder.AddPolicyHandler(Policy.HandleInner().FallbackAsync(fallbackResponse, async b =>
{
// 1、降级打印异常
Console.WriteLine($"服务开始降级,异常消息:{b.Exception.Message}");
// 2、降级后的数据
//Console.WriteLine($"服务降级内容响应:{fallbackResponse.Content.ToString()}");
await Task.CompletedTask;
}));
}
//熔断
private static void BuildCircuitBreakerAsync(this IHttpClientBuilder builder, int circuitBreakerOpenFallCount, int circuitBreakerDownTime)
{
if (circuitBreakerOpenFallCount < 1 || circuitBreakerDownTime < 1)
return;
builder.AddPolicyHandler(
Policy // HttpResponseMessage 为HttpClient的返回值
.Handle() //捕获Exception异常
.CircuitBreakerAsync(
circuitBreakerOpenFallCount, // 出现3次异常
TimeSpan.FromSeconds(circuitBreakerDownTime), //10秒之内; 结合上面就是:10秒之内出现3次异常就熔断
(ex, ts) =>
{ //熔断器开启事件触发
Console.WriteLine($"服务断路器开启,异常消息:{ex.Exception.Message}");
Console.WriteLine($"服务断路器开启的时间:{ts.TotalSeconds}s");
}, //断路器重置事件触发
() => { Console.WriteLine($"服务断路器重置"); }, //断路器半开启事件触发
() => { Console.WriteLine($"服务断路器半开启(一会开,一会关)"); }
)
);
}
//失败重试
private static void BuildRetryAsync(this IHttpClientBuilder builder, int retryCount)
{
if (retryCount > 0)//失败重试
builder.AddPolicyHandler(Policy.Handle().RetryAsync(retryCount));
}
//超时
private static void BuildTimeoutAsync(this IHttpClientBuilder builder, int timeoutTime)
{
if (timeoutTime > 0)//超时
builder.AddPolicyHandler(Policy.TimeoutAsync(TimeSpan.FromSeconds(timeoutTime)));
}
}
2.4 在startup.cs类的ConfigureServices方法中使用
services.AddPollyHttpClient();