• 熔断降级(Polly)


    熔断降级(Polly)

    https://www.cnblogs.com/qhbm/p/9224307.html

    一、 什么是熔断降级
    熔断就是“保险丝”。当出现某些状况时,切断服务,从而防止应用程序不断地尝试执行可能会失败的操作给系统造成“雪崩”,或者大量的超时等待导致系统卡死。

    降级的目的是当某个服务提供者发生故障的时候,向调用方返回一个错误响应或者替代响应。举例子:调用联通接口服务器发送短信失败之后,改用移动短信服务器发送,如果移动短信服务器也失败,则改用电信短信服务器,如果还失败,则返回“失败”响应;在从推荐商品服务器加载数据的时候,如果失败,则改用从缓存中加载,如果缓存中也加载失败,则返回一些本地替代数据。

    二、 Polly 简介
    .Net Core 中有一个被.Net 基金会认可的库 Polly,可以用来简化熔断降级的处理。主要功能:重试(Retry);断路器(Circuit-breaker);超时检测(Timeout);缓存(Cache);降级(FallBack);

    官网:https://github.com/App-vNext/Polly

    介绍文章:https://www.cnblogs.com/CreateMyself/p/7589397.html

    Nuget安装指令:Install-Package Polly -Version 6.0.1

    Polly 的策略由“故障”和“动作”两部分组成,“故障”包括异常、超时、返回值错误等情况,“动作”包括 降级(FallBack)、重试(Retry)、熔断(Circuit-breaker)等。

    策略用来执行可能会有有故障的业务代码,当业务代码出现“故障”中情况的时候就执行“动作”。

    由于实际业务代码中故障情况很难重现出来,所以 Polly 这一些都是用一些无意义的代码模拟出来。

    Polly 也支持请求缓存“数据不变化则不重复自行代码”,但是和新版本兼容不好,而且功能局限性很大,因此这里不讲。

    由于调试器存在,看不清楚 Polly 的执行过程,因此本节都用【开始执行(不调试)】

    三、Polly简单使用
    使用Policy的静态方法创建ISyncPolicy实现类对象,创建方法既有同步方法也有异步方法,根据自己的需要选择。下面先演示同步的,异步的用法类似。

    举例:当发生ArgumentException异常的时候,执行Fallback代码。

    复制代码
    Policy policy = Policy
    .Handle() //故障
    .Fallback(() =>//动作
    {
      Console.WriteLine("执行出错");
    });
    policy.Execute(() => {//在策略中执行业务代码
    //这里是可能会产生问题的业务系统代码
    Console.WriteLine("开始任务");  throw new ArgumentException("Hello world!");
      Console.WriteLine("完成任务");
    });
    Console.ReadKey();
    复制代码
    如果没有被Handle处理的异常,则会导致未处理异常被抛出。

    还可以用Fallback的其他重载获取异常信息:

    复制代码
    Policy policy = Policy
    .Handle() //故障
    .Fallback(() =>//动作
    {
      Console.WriteLine("执行出错");
    },ex=> {
      Console.WriteLine(ex);
    });
    policy.Execute(() => {
      //在策略中执行业务代码
      //这里是可能会产生问题的业务系统代码
      Console.WriteLine("开始任务1");
      throw new ArgumentException("Hello1 world!");
      Console.WriteLine("完成任务");
    });
    复制代码
    如果Execute中的代码是带返回值的,那么只要使用带泛型的Policy类即可:

    复制代码
    Policy policy = Policy
    .Handle() //故障
    .Fallback(() =>//动作
    {
      Console.WriteLine("执行出错");
      return "降级的值";
    });
    string value = policy.Execute(() => {
      Console.WriteLine("开始任务");
      throw new Exception("Hello world!");
      Console.WriteLine("完成任务");
      return "正常的值";
    });
    Console.WriteLine("返回值:"+value);
    复制代码
    FallBack的重载方法也非常多,有的异常可以直接提供降级后的值。

    ()异常中还可以通过lambda表达式对异常判断“满足**条件的异常我才处理”,简单看看试试重载即可。还可以多个Or处理各种不同的异常。

    (*)还可以用HandleResult等判断返回值进行故障判断等,我感觉没太大必要。

    四、重试处理
    复制代码
    Policy policy = Policy
    .Handle()
    .RetryForever();
    policy.Execute(() => {
    Console.WriteLine("开始任务");
    if (DateTime.Now.Second % 10 != 0)
    {
    throw new Exception("出错");
    }
    Console.WriteLine("完成任务");
    });
    复制代码
    RetryForever()是一直重试直到成功

    Retry()是重试最多一次;

    Retry(n) 是重试最多n次;

    WaitAndRetry()可以实现“如果出错等待100ms再试还不行再等150ms秒。。。。”,重载方法很多,不再一一介绍。还有WaitAndRetryForever。

    五、 短路保护 Circuit Breaker
      出现N次连续错误,则把“熔断器”(保险丝)熔断,等待一段时间,等待这段时间内如果再Execute 则直接抛出BrokenCircuitException异常,根本不会再去尝试调用业务代码。等待时间过去之后,再执行Execute的时候如果又错了(一次就够了),那么继续熔断一段时间,否则就恢复正常。

    这样就避免一个服务已经不可用了,还是使劲的请求给系统造成更大压力。

    复制代码
    Policy policy = Policy
    .Handle()
    .CircuitBreaker(6,TimeSpan.FromSeconds(5));//连续出错6次之后熔断5秒(不会再去尝试执行业务代码)。
    while(true)
    {
    Console.WriteLine("开始Execute");
    try
    {
    policy.Execute(() => {
    Console.WriteLine("开始任务");
    throw new Exception("出错");
    Console.WriteLine("完成任务");
    });
    }
    catch(Exception ex)
    {
    Console.WriteLine("execute出错"+ex);
    }
    Thread.Sleep(500);
    }
    复制代码
    其计数的范围是policy对象,所以如果想整个服务器全局对于一段代码做短路保护,则需要共用一个policy对象。

    六、策略封装,包裹Warp
    可以把多个ISyncPolicy合并到一起执行:

    policy3= policy1.Wrap(policy2);
    执行policy3就会把policy1、policy2封装到一起执行。

    Policy的静态方法Wrap可以把更多的policy一起封装:

    policy9=Policy.Wrap(policy1, policy2, policy3, policy4, policy5);
    七、超时处理
    这些处理不能简单的链式调用,要用到Wrap。例如下面实现“出现异常则重试三次,如果还出错就FallBack”这样是不行的

    Policy policy = Policy.Handle().Retry(3).Fallback(()=> { Console.WriteLine("执行出错"); });//这样不行,系统会直接报错

    注意Wrap是有包裹顺序的,内层的故障如果没有被处理则会抛出到外层。

    下面代码实现了“出现异常则重试三次,如果还出错就FallBack”

    复制代码
    Policy policyRetry = Policy.Handle().Retry(3); //出现异常重试三次
    Policy policyFallback = Policy
    .Handle()
    .Fallback(()=> {
    Console.WriteLine("降级");
    });
    //Wrap:包裹。policyRetry在里面,policyFallback裹在外面。
    //如果里面出现了故障,则把故障抛出来给外面
    Policy policy = policyFallback.Wrap(policyRetry);
    policy.Execute(()=> {
    Console.WriteLine("开始任务");
    if (DateTime.Now.Second % 10 != 0)
    {
       throw new Exception("出错");
    }
      Console.WriteLine("完成任务");
    });
    复制代码
    运行结果:

    Timeout是定义超时故障,如果超时会抛出TimeoutRejectedException异常。

    Policy policy = Policy.Timeout(3, TimeoutStrategy.Pessimistic);// 创建一个3秒钟(注意单位)的超时策略。
    Timeout生成的Policy要和其他Policy一起Wrap使用。

    超时策略一般不能直接用,而是和其他封装到一起用:

    复制代码
    Policy policy = Policy
    .Handle() //定义所处理的故障
    .Fallback(() =>
    {
    Console.WriteLine("降级");
    });
    policy = policy.Wrap(Policy.Timeout(2,TimeoutStrategy.Pessimistic));
    policy.Execute(()=> {
    Console.WriteLine("开始任务");
    Thread.Sleep(5000);
    Console.WriteLine("完成任务");
    });
    复制代码
    执行结果:

    上面的代码就是如果执行超过2秒钟,则直接Fallback。 这个的用途:请求网络接口,避免接口长期没有响应造成系统卡死。

    八、Polly 的异步用法
    所有方法都用Async方法即可,Handle由于只是定义异常,所以不需要异常方法:

    带返回值的例子:

    复制代码
    Policy<byte[]> policy = Policy<byte[]>
    .Handle()
    .FallbackAsync(async c => {
    Console.WriteLine("降级");
    return new byte[0];
    },async r=> {
    Console.WriteLine(r.Exception);
    });
    policy = policy.WrapAsync(
      Policy.TimeoutAsync(2, TimeoutStrategy.Pessimistic, async(context, timespan, task) =>
      {
      Console.WriteLine("timeout");
      })
    );
    var bytes = await policy.ExecuteAsync(async () => {
    Console.WriteLine("开始任务");
    HttpClient httpClient = new HttpClient();
    var result = await httpClient.GetByteArrayAsync("http://static.rupeng.com/upload/chatimage/20183/07EB793A4C247A654B31B4D14EC64BCA.png");
    Console.WriteLine("完成任务");
    return result;
    });
    Console.WriteLine("bytes长度"+bytes.Length);
    复制代码
    执行结果:

    没返回值的例子:

    复制代码
    Policy policy = Policy
    .Handle()
    .FallbackAsync(async c => {
    Console.WriteLine("降级");
    },async ex=> {//对于没有返回值的,这个参数直接是异常
    Console.WriteLine(ex);
    });
    policy = policy.WrapAsync(Policy.TimeoutAsync(3, TimeoutStrategy.Pessimistic, async(context, timespan, task) =>
    {
    Console.WriteLine("timeout");
    }));
    await policy.ExecuteAsync(async () => {
    Console.WriteLine("开始任务");
    await Task.Delay(5000);//注意不能用Thread.Sleep(5000);
    Console.WriteLine("完成任务");
    });
    复制代码
    执行结果:

  • 相关阅读:
    死磕 java同步系列之Phaser源码解析
    死磕 java同步系列之CyclicBarrier源码解析——有图有真相
    死磕 java同步系列之StampedLock源码解析
    死磕 java同步系列之AQS终篇(面试)
    死磕 java同步系列之Semaphore源码解析
    网速监控-nload
    权限(rwx)对于目录与文件的意义
    maven自定义archetype
    -------------------新的开始-------------------
    GitFlow入门
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/9251672.html
Copyright © 2020-2023  润新知