zoukankan      html  css  js  c++  java
  • Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Ocelot+Polly缓存、限流、熔断、降级

    相关文章

    Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Consul服务注册,服务发现

    Consul+Ocelot+Polly在.NetCore中使用(.NET5)-网关Ocelot+Consul

    Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Ocelot+Polly缓存、限流、熔断、降级

    微服务网关Ocelot加入IdentityServer4鉴权-.NetCore(.NET5)中使用

    环境Ocelot包版本17.0.0

    一、简介

    Polly 是一个 .NET 弹性和瞬态故障处理库,允许开发人员以线程安全的方式来实现重试、断路、超时、隔离和回退策略。

    瞬态故障就是可能会出现的,比喻网络不稳定或无法访问,或服务宕机。

    二、Ocelot各种策略使用和解释

    下面各种策略都是基于前一篇Ocelot+Consul的配置基础上修改。

    2.1Ocelot缓存

    缓存能有效提升程序性能

    在ocelot.json中增加缓存配置

      {
        "Routes": [
          {
            //转发到下游服务地址--url变量
            "DownstreamPathTemplate": "/api/{url}",
            //下游http协议
            "DownstreamScheme": "http",
            //负载方式,
            "LoadBalancerOptions": {
              "Type": "RoundRobin" // 轮询
            },
            //上游地址
            "UpstreamPathTemplate": "/T1/{url}", //网关地址--url变量   //冲突的还可以加权重Priority
            "UpstreamHttpMethod": [ "GET", "POST", "DELETE", "PUT" ],
            "UseServiceDisConvery": true, //使用服务发现
            "ServiceName": "api", //Consul服务名称
            //缓存设置
            "FileCacheOptions": {
              "TtlSeconds": 10, //缓存10s(同一个地址请求就返回缓存结果)
              "Region": ""//缓存region
            }
          }
        ],
        "GlobalConfiguration": {
          //Ocelot应用地址
          "BaseUrl": "http://172.16.2.9:5200",
          "ServiceDiscoveryProvider": {
            //Consul地址
            "Host": "172.16.2.84",
            //Consul端口
            "Port": 8500,
            "Type": "Consul"//由Consul提供服务发现,每次请求Consul
          }
        }
      }

    缓存是针对下游地址缓存的,同一个地址请求返回相同数据,所以针对一些不变的数据才能做缓存,根据用户登录信息不同返回不同数据的就不能做了。

    测试:访问 http://172.16.2.9:5200/T1/Test/GetName

     刷新后还是5201端口数据,说明是从缓存取的

    10s后刷新端口变成5202

    2.2Ocelot限流

    为什么要限流呢,防止请求过多把程序搞宕机了,也可以有效防止爬虫和ddos攻击,预估出服务的处理能力,然后设置限流,可以限制单位时间内的访问量(失败一部分请求比整个服务挂掉强)。

    ocelot.json文件增加限流配置

      {
        "Routes": [
          {
            //转发到下游服务地址--url变量
            "DownstreamPathTemplate": "/api/{url}",
            //下游http协议
            "DownstreamScheme": "http",
            //负载方式,
            "LoadBalancerOptions": {
              "Type": "RoundRobin" // 轮询
            },
            //上游地址
            "UpstreamPathTemplate": "/T1/{url}", //网关地址--url变量   //冲突的还可以加权重Priority
            "UpstreamHttpMethod": [ "GET", "POST", "DELETE", "PUT" ],
            "UseServiceDisConvery": true, //使用服务发现
            "ServiceName": "api", //Consul服务名称
            //限流配置
            "RateLimitOptions": {
              "ClientWhitelist": [ "admin" ], // 白名单
              "EnableRateLimiting": true, // 是否启用限流
              "Period": "10s", // 统计时间段:1s, 5m, 1h, 1d
              "PeriodTimespan": 10, // 多少秒之后客户端可以重试
              "Limit": 5 // 在统计时间段内允许的最大请求数量
            }
          }
        ],
        "GlobalConfiguration": {
          //Ocelot应用地址
          "BaseUrl": "http://172.16.2.9:5200",
          "ServiceDiscoveryProvider": {
            //Consul地址
            "Host": "172.16.2.84",
            //Consul端口
            "Port": 8500,
            "Type": "Consul" //由Consul提供服务发现,每次请求Consul
          },
          //限流
          "RateLimitOptions": {
            "QuotaExceededMessage": "errr:request fast!", //限流后响应内容
            "HttpStatusCode": 666, //http状态码可以自定义
         "ClientIdHeader": "client_id" // 用来识别客户端的请求头,默认是 ClientId
    } } }

    这里用Postman来演示比较直观。

     可以看到,在10s内请求了5次之后的请求就失败了,返回的状态码是自定义的666,然后等10s过后又恢复访问,上面设置的白名单在Headers加上就可以

    不受限流影响,可以无限访问。

    2.3Ocelot+Polly的熔断

    安装NuGet包 Ocelot.Provider.Polly。

    Startup.cs增加 .AddPolly()

    public void ConfigureServices(IServiceCollection services)
            {
                services.AddOcelot()
                    .AddConsul()
                    .AddPolly();
            }

    Ocelot.Provider.Polly的熔断机制是一个超时和熔断的组合,(Polly有超时策略,熔断策略,这里是2个策略的结合使用,下面Polly策略会说到),所以如果是单单是服务报500异常是触发不了的

    接口超过多长时间进入半熔断状态,返回服务不可用, 连续超过多少次进入熔断状态就直接停掉该请求返回,多长时间再恢复。

    修改ocelot.json配置

    //*****************************单地址********************************
      {
        "Routes": [
          {
            //转发到下游服务地址--url变量
            "DownstreamPathTemplate": "/api/{url}",
            //下游http协议
            "DownstreamScheme": "http",
            //负载方式,
            "LoadBalancerOptions": {
              "Type": "RoundRobin" // 轮询
            },
            //上游地址
            "UpstreamPathTemplate": "/T1/{url}", //网关地址--url变量   //冲突的还可以加权重Priority
            "UpstreamHttpMethod": [ "GET", "POST", "DELETE", "PUT" ],
            "UseServiceDisConvery": true, //使用服务发现
            "ServiceName": "api", //Consul服务名称
            //熔断设置
            "QoSOptions": {
              "ExceptionsAllowedBeforeBreaking": 3, //允许多少个异常请求
              "DurationOfBreak": 5000, // 熔断的时间5s,单位为ms
              "TimeoutValue": 5000 //单位ms,如果下游请求的处理时间超过多少则自如将请求设置为超时 默认90秒
            }
          }
        ],
        "GlobalConfiguration": {
          //Ocelot应用对外地址
          "BaseUrl": "http://172.16.2.9:5200",
          "ServiceDiscoveryProvider": {
            //Consul地址
            "Host": "172.16.2.84",
            //Consul端口
            "Port": 8500,
            "Type": "Consul" //由Consul提供服务发现,每次请求Consul
          }
        }
      }

     在之前启动的3个服务增加一个抛异常的接口和一个睡眠接口。

     [Route("api/[controller]/[action]")]
        public class TestController : Controller
        {
            private IConfiguration _configuration;
            public TestController(IConfiguration configuration)
            {
                _configuration = configuration;
            }
            public IActionResult GetName()
            {
                string port = _configuration["port"];
                return Json($"端口:{port},姓名:张三");
            }public IActionResult GetSleep()
            {
                string port = _configuration["port"];
    
                //线程睡眠6s
                Thread.Sleep(6000);
                return Json($"端口:{port},睡眠6s后返回");
            }
        }

    访问GetSleep()接口,前三次等待6s返回503,后面访问什么接口都是快速返回503,服务熔断。

    三、Polly各种策略使用和解释

    上面网关处做了Ocelot+Polly的熔断策略,然后服务链上也是需要做一些策略,这里介绍的是在服务里用Polly做各种常用的策略。

    3.1Polly降级

    降级就是当我们指定的代码处理失败时就执行我们备用的代码。

    现在在之前的三个服务中加入Polly降级策略

    安装NuGet包 --Polly

    新建一个OrderController.cs

     [Route("api/[controller]/[action]")]
        public class OrderController : Controller
        {
            private readonly OrderService _orderService;
            public OrderController(OrderService orderService)
            {
                _orderService = orderService;
            }
            public IActionResult CreateOrder()
            {
                string result = _orderService.CreateOrder();
                return Content(result);
            }
        }

    新建一个OrderService.cs

      public class OrderService
        {
            private Policy<string> _policy;
            public OrderService()
            {
                //降级
                _policy = Policy<string>
                    .Handle<Exception>() //异常故障
                    .Fallback(() =>
                    {
                        //降级回调 todo降级后逻辑
                        return "降级后的值";
                    });
    
            }
            public string CreateOrder()
            {
                //用polly执行
                return _policy.Execute(() =>
                {
                    //业务逻辑 todo
                    Console.WriteLine($"{DateTime.Now},开始处理业务");
    
                    throw new Exception("233出错啦");
                    Console.WriteLine("处理完成");
                    return "成功啦";
                });
            }
        }

    把OrderService注入IOC容器,注意,使用Polly时,实例一定要以单例模式注入,因为如果是每次都执行构造函数给_policy赋一次值,那_policy每次都是全新的,下面的熔断策畋会不生效。

    Startup.cs中的ConfigureServices()加上

     public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllersWithViews();
                services.AddControllers().AddJsonOptions(cfg =>
                {
                services.AddSingleton<OrderService>();
            }

    测试,访问http://ip:端口/api/Order/CreateOrder

    可以看到返回的是降级后处理的值。

    3.2Polly熔断

    熔断就是当一处代码报错超过多少次,就让它熔断多长时间再恢复,熔断时Polly会截断请求,不会再进入到具体业务,这能有效减少没必要的业务性能损耗。

    把OrderService构建函数处改成

    public OrderService()
            {
                //熔断
                _policy = Policy<string>.Handle<Exception>()
                    .CircuitBreaker(5, TimeSpan.FromSeconds(10));//连续出错5次后熔断10秒,不会在进到业务代码
    
            }

    测试结果:

    5次前的返回:

     熔断后返回:

    3.3Polly重试

    把OrderService的构造函数处修改为:

      public OrderService()
            {
                //重试
                //RetryForever()是一直重试直到成功
                //Retry()是重试最多一次;
                //Retry(n) 是重试最多n次;
                //WaitAndRetry()可以实现“如果出错等待100ms再试还不行再等150ms秒。
                 _policy = Policy<string>.Handle<Exception>()
                       .WaitAndRetry(new TimeSpan[] { TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(15) });
    
            }

    这里是业务处理失败时,重试3次,分别隔5s,10s,15s。

    测试结果:

     可以看到,第一次执行因为有异常,然后分别隔5s,10s,15s重试,最后才抛出了异常。

    3.4Polly超时

    所谓超时,就是我们指定一段代码的最大运行时间,如果超过这段时间还没有完成,就直接抛出异常。
    这里判断超时有两种策略:一个是悲观策略(Pessimistic),一个是乐观策略(Optimistic)。一般我们用悲观策略。需要注意的是,虽然超时抛除了异常,但这段代码的运行并没有停止!

    把OrderService构建函数处改成

      public OrderService()
            {
                //超时,业务处理超过3秒就直接返回异常
                _policy = Policy.Timeout<string>(3, Polly.Timeout.TimeoutStrategy.Pessimistic);
            }

    把OrderService.cs的CreateOrder()方法让线程睡眠10s

     public string CreateOrder()
            {
                //用polly执行
                return _policy.Execute(() =>
                {
                    //业务逻辑 todo
                    Console.WriteLine($"{DateTime.Now},开始处理业务");
                    Thread.Sleep(10000); //睡眠10s
                    Console.WriteLine("处理完成");
                    return "成功啦";
                });
            }

    执行查看结果:

     可以看到,在3s的时候报了polly的超时异常。

    3.5Polly组合策略

     上面说的都是单个策略的,其实这些策略是可以组合一起使用的,下面来演示一下。

    把OrderService的构造函数修改为:

     public OrderService()
            {
                //重试
                Policy<string> retry = Policy<string>.Handle<Exception>()
                  .WaitAndRetry(new TimeSpan[] { TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(15) });
    
                //降级
                Policy<string> fallback = Policy<string>
                     .Handle<Exception>() //异常故障
                     .Fallback(() =>
                     {
                        //降级回调
                        return "降级后的值";
                     });
                //Wrap:包裹。policyRetry在里面,policyFallback裹在外面。
                //如果里面出现了故障,则把故障抛出来给外面
                //_policy=Policy.Wrap(policy1, policy2, policy3, policy4, policy5);把更多一起封装。
                _policy = Policy.Wrap(fallback, retry); // fallback.Wrap<string>(retry);
            }

    把CreateOrder()修改为

     public string CreateOrder()
            {
                //用polly执行
                return _policy.Execute(() =>
                {
                    //业务逻辑 todo
                    Console.WriteLine($"{DateTime.Now},开始处理业务");
                    throw new Exception("233出错啦");
                    Console.WriteLine("处理完成");
                    return "成功啦";
                });
            }

    测试结果:

     重试3次后,返回降级的结果。

    上面的Ocelot+Polly的熔断如果去查看Ocelot.Provider.Polly的源码就会发现是超时和熔断的组合实现。

    需要注意的是,组合时Policy.Wrap(fallback, retry);里面的顺序也要注意,测试结果是先执行后面的,再执行前面的,即前面的把后面的包在内层,内层执行完再抛出给外层处理。

    上面介绍了最常用的策略,Polly也有异步的方法,把上面定义的private Policy<string> _policy; 改成 private AsyncPolicy<string> _policy; 即可。

    源码地址:https://github.com/weixiaolong325/Ocelot-Consul-Polly-Id4.Demo

  • 相关阅读:
    递归函数及Java范例
    笔记本的硬盘坏了
    “References to generic type List should be parameterized”
    配置管理软件(configuration management software)介绍
    WinCE文件目录定制及内存调整
    使用Silverlight for Embedded开发绚丽的界面(3)
    wince国际化语言支持
    Eclipse IDE for Java EE Developers 与Eclipse Classic 区别
    WinCE Heartbeat Message的实现
    使用Silverlight for Embedded开发绚丽的界面(2)
  • 原文地址:https://www.cnblogs.com/wei325/p/15308498.html
Copyright © 2011-2022 走看看