说明:

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<HttpResponseMessage>.HandleInner<Exception>().FallbackAsync(fallbackResponse, async b =>
    {
        // 1、降级打印异常
        Console.WriteLine($"服务开始降级,异常消息:{b.Exception.Message}");
        // 2、降级后的数据
        //Console.WriteLine($"服务降级内容响应:{fallbackResponse.Content.ToString()}");
        await Task.CompletedTask;
    }))
    //熔断                                                      
    .AddPolicyHandler(
        Policy<HttpResponseMessage> // HttpResponseMessage 为HttpClient的返回值
        .Handle<Exception>() //捕获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<HttpResponseMessage>.Handle<Exception>().RetryAsync(3))//失败重试三次
    //超时
    .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(2)));//设置2秒超时时间

2、使用HttpClient来发起请求

private readonly IHttpClientFactory _httpClientFactory;
public TeamService(IHttpClientFactory httpClientFactory)
{
    _httpClientFactory = httpClientFactory;
}
public async Task<TeamInfo> 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
{
    /// <summary>
    /// 服务名称
    /// </summary>
    public List<string> ServiceName { set; get; }

    /// <summary>
    /// 超时时间设置,单位为秒
    /// </summary>
    public int TimeoutTime { set; get; }

    /// <summary>
    /// 失败重试次数
    /// </summary>
    public int RetryCount { set; get; }

    /// <summary>
    /// 执行多少次异常,开启短路器(例:失败2次,开启断路器)
    /// </summary>
    public int CircuitBreakerOpenFallCount { set; get; }

    /// <summary>
    /// 断路器关闭的时间(例如:设置为2秒,短路器两秒后自动由开启到关闭)
    /// </summary>
    public int CircuitBreakerDownTime { set; get; }

    /// <summary>
    /// 降级处理消息(将异常消息封装成为正常消息返回,然后进行响应处理,例如:系统正在繁忙,请稍后处理.....)
    /// </summary>
    public string HttpResponseMessage { set; get; }
    /// <summary>
    /// 降级处理状态码(将异常消息封装成为正常消息返回,然后进行响应处理,例如:系统正在繁忙,请稍后处理.....)
    /// </summary>
    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<PollyHttpClientConfig> configList = config.GetSection("Polly").Get<List<PollyHttpClientConfig>>(); // 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<HttpResponseMessage>.HandleInner<Exception>().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> // HttpResponseMessage 为HttpClient的返回值
                           .Handle<Exception>() //捕获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<HttpResponseMessage>.Handle<Exception>().RetryAsync(retryCount));
        }
        //超时
        private static void BuildTimeoutAsync(this IHttpClientBuilder builder, int timeoutTime)
        {
            if (timeoutTime > 0)//超时
                builder.AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(timeoutTime)));
        }
    }

2.4 在startup.cs类的ConfigureServices方法中使用

services.AddPollyHttpClient();

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐