1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        .NET 5 中使用 Consul+Ocelot+Polly緩存、限流、熔斷、降級

        共 9188字,需瀏覽 19分鐘

         ·

        2021-10-26 17:46


        一、簡介

        Polly 是一個 .NET 彈性和瞬態(tài)故障處理庫,允許開發(fā)人員以線程安全的方式來實現(xiàn)重試、斷路、超時、隔離和回退策略。

        瞬態(tài)故障就是可能會出現(xiàn)的,比喻網(wǎng)絡(luò)不穩(wěn)定或無法訪問,或服務(wù)宕機(jī)。

        二、Ocelot各種策略使用和解釋

        下面各種策略都是基于前一篇Ocelot+Consul的配置基礎(chǔ)上修改。

        2.1、Ocelot緩存

        緩存能有效提升程序性能

        在ocelot.json中增加緩存配置

        ??{
        ????"Routes":?[
        ??????{
        ????????//轉(zhuǎn)發(fā)到下游服務(wù)地址--url變量
        ????????"DownstreamPathTemplate":?"/api/{url}",
        ????????//下游http協(xié)議
        ????????"DownstreamScheme":?"http",
        ????????//負(fù)載方式,
        ????????"LoadBalancerOptions":?{
        ??????????"Type":?"RoundRobin"?//?輪詢
        ????????},
        ????????//上游地址
        ????????"UpstreamPathTemplate":?"/T1/{url}",?//網(wǎng)關(guān)地址--url變量???//沖突的還可以加權(quán)重Priority
        ????????"UpstreamHttpMethod":?[?"GET",?"POST",?"DELETE",?"PUT"?],
        ????????"UseServiceDisConvery":?true,?//使用服務(wù)發(fā)現(xiàn)
        ????????"ServiceName":?"api",?//Consul服務(wù)名稱
        ????????//緩存設(shè)置
        ????????"FileCacheOptions":?{
        ??????????"TtlSeconds":?10,?//緩存10s(同一個地址請求就返回緩存結(jié)果)
        ??????????"Region":?""//緩存region
        ????????}
        ??????}
        ????],
        ????"GlobalConfiguration":?{
        ??????//Ocelot應(yīng)用地址
        ??????"BaseUrl":?"http://172.16.2.9:5200",
        ??????"ServiceDiscoveryProvider":?{
        ????????//Consul地址
        ????????"Host":?"172.16.2.84",
        ????????//Consul端口
        ????????"Port":?8500,
        ????????"Type":?"Consul"//由Consul提供服務(wù)發(fā)現(xiàn),每次請求Consul
        ??????}
        ????}
        ??}

        緩存是針對下游地址緩存的,同一個地址請求返回相同數(shù)據(jù),所以針對一些不變的數(shù)據(jù)才能做緩存,根據(jù)用戶登錄信息不同返回不同數(shù)據(jù)的就不能做了。

        測試:訪問 http://172.16.2.9:5200/T1/Test/GetName

        刷新后還是5201端口數(shù)據(jù),說明是從緩存取的

        10s后刷新端口變成5202

        2.2、Ocelot限流

        為什么要限流呢,防止請求過多把程序搞宕機(jī)了,也可以有效防止爬蟲和ddos攻擊,預(yù)估出服務(wù)的處理能力,然后設(shè)置限流,可以限制單位時間內(nèi)的訪問量(失敗一部分請求比整個服務(wù)掛掉強(qiáng))。

        ocelot.json文件增加限流配置

        ??{
        ????"Routes":?[
        ??????{
        ????????//轉(zhuǎn)發(fā)到下游服務(wù)地址--url變量
        ????????"DownstreamPathTemplate":?"/api/{url}",
        ????????//下游http協(xié)議
        ????????"DownstreamScheme":?"http",
        ????????//負(fù)載方式,
        ????????"LoadBalancerOptions":?{
        ??????????"Type":?"RoundRobin"?//?輪詢
        ????????},
        ????????//上游地址
        ????????"UpstreamPathTemplate":?"/T1/{url}",?//網(wǎng)關(guān)地址--url變量???//沖突的還可以加權(quán)重Priority
        ????????"UpstreamHttpMethod":?[?"GET",?"POST",?"DELETE",?"PUT"?],
        ????????"UseServiceDisConvery":?true,?//使用服務(wù)發(fā)現(xiàn)
        ????????"ServiceName":?"api",?//Consul服務(wù)名稱
        ????????//限流配置
        ????????"RateLimitOptions":?{
        ??????????"ClientWhitelist":?[?"admin"?],?//?白名單
        ??????????"EnableRateLimiting":?true,?//?是否啟用限流
        ??????????"Period":?"10s",?//?統(tǒng)計時間段:1s, 5m, 1h, 1d
        ??????????"PeriodTimespan":?10,?//?多少秒之后客戶端可以重試
        ??????????"Limit":?5?//?在統(tǒng)計時間段內(nèi)允許的最大請求數(shù)量
        ????????}
        ??????}
        ????],
        ????"GlobalConfiguration":?{
        ??????//Ocelot應(yīng)用地址
        ??????"BaseUrl":?"http://172.16.2.9:5200",
        ??????"ServiceDiscoveryProvider":?{
        ????????//Consul地址
        ????????"Host":?"172.16.2.84",
        ????????//Consul端口
        ????????"Port":?8500,
        ????????"Type":?"Consul"?//由Consul提供服務(wù)發(fā)現(xiàn),每次請求Consul
        ??????},
        ??????//限流
        ??????"RateLimitOptions":?{
        ????????"QuotaExceededMessage":?"errr:request?fast!",?//限流后響應(yīng)內(nèi)容
        ????????"HttpStatusCode":?666,?//http狀態(tài)碼可以自定義?????"ClientIdHeader":?"client_id"?//?用來識別客戶端的請求頭,默認(rèn)是?ClientId
        ??????}
        ????}
        ??}

        這里用Postman來演示比較直觀。

        可以看到,在10s內(nèi)請求了5次之后的請求就失敗了,返回的狀態(tài)碼是自定義的666,然后等10s過后又恢復(fù)訪問,上面設(shè)置的白名單在Headers加上就可以

        不受限流影響,可以無限訪問。

        2.3、Ocelot+Polly的熔斷

        安裝NuGet包 Ocelot.Provider.Polly。

        Startup.cs增加 .AddPolly()

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

        Ocelot.Provider.Polly的熔斷機(jī)制是一個超時和熔斷的組合,(Polly有超時策略,熔斷策略,這里是2個策略的結(jié)合使用,下面Polly策略會說到),所以如果是單單是服務(wù)報500異常是觸發(fā)不了的。

        接口超過多長時間進(jìn)入半熔斷狀態(tài),返回服務(wù)不可用, 連續(xù)超過多少次進(jìn)入熔斷狀態(tài)就直接停掉該請求返回,多長時間再恢復(fù)。

        修改ocelot.json配置

        ??//*****************************單地址********************************
        ??{
        ????"Routes":?[
        ??????{
        ????????//轉(zhuǎn)發(fā)到下游服務(wù)地址--url變量
        ????????"DownstreamPathTemplate":?"/api/{url}",
        ????????//下游http協(xié)議
        ????????"DownstreamScheme":?"http",
        ????????//負(fù)載方式,
        ????????"LoadBalancerOptions":?{
        ??????????"Type":?"RoundRobin"?//?輪詢
        ????????},
        ????????//上游地址
        ????????"UpstreamPathTemplate":?"/T1/{url}",?//網(wǎng)關(guān)地址--url變量???//沖突的還可以加權(quán)重Priority
        ????????"UpstreamHttpMethod":?[?"GET",?"POST",?"DELETE",?"PUT"?],
        ????????"UseServiceDisConvery":?true,?//使用服務(wù)發(fā)現(xiàn)
        ????????"ServiceName":?"api",?//Consul服務(wù)名稱
        ????????//熔斷設(shè)置
        ????????"QoSOptions":?{
        ??????????"ExceptionsAllowedBeforeBreaking":?3,?//允許多少個異常請求
        ??????????"DurationOfBreak":?5000,?//?熔斷的時間5s,單位為ms
        ??????????"TimeoutValue":?5000?//單位ms,如果下游請求的處理時間超過多少則自如將請求設(shè)置為超時?默認(rèn)90秒
        ????????}
        ??????}
        ????],
        ????"GlobalConfiguration":?{
        ??????//Ocelot應(yīng)用對外地址
        ??????"BaseUrl":?"http://172.16.2.9:5200",
        ??????"ServiceDiscoveryProvider":?{
        ????????//Consul地址
        ????????"Host":?"172.16.2.84",
        ????????//Consul端口
        ????????"Port":?8500,
        ????????"Type":?"Consul"?//由Consul提供服務(wù)發(fā)現(xiàn),每次請求Consul
        ??????}
        ????}
        ??}

        在之前啟動的3個服務(wù)增加一個拋異常的接口和一個睡眠接口。

        ?[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,服務(wù)熔斷。

        三、Polly各種策略使用和解釋

        上面網(wǎng)關(guān)處做了Ocelot+Polly的熔斷策略,然后服務(wù)鏈上也是需要做一些策略,這里介紹的是在服務(wù)里用Polly做各種常用的策略。

        3.1、Polly降級

        降級就是當(dāng)我們指定的代碼處理失敗時就執(zhí)行我們備用的代碼。

        現(xiàn)在在之前的三個服務(wù)中加入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()?//異常故障
        ??????????????.Fallback(()?=>
        ??????????????{
        ??????????????????//降級回調(diào)?todo降級后邏輯
        ??????????????????return?"降級后的值";
        ??????????????});

        ??????}
        ??????public?string?CreateOrder()
        ??????{
        ??????????//用polly執(zhí)行
        ??????????return?_policy.Execute(()?=>
        ??????????{
        ??????????????//業(yè)務(wù)邏輯?todo
        ??????????????Console.WriteLine($"{DateTime.Now},開始處理業(yè)務(wù)");

        ??????????????throw?new?Exception("233出錯啦");
        ??????????????Console.WriteLine("處理完成");
        ??????????????return?"成功啦";
        ??????????});
        ??????}
        ??}

        把OrderService注入IOC容器,注意,使用Polly時,實例一定要以單例模式注入,因為如果是每次都執(zhí)行構(gòu)造函數(shù)給_policy賦一次值,那_policy每次都是全新的,下面的熔斷策畋會不生效。

        Startup.cs中的ConfigureServices()加上

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

        測試,訪問http://ip:端口/api/Order/CreateOrder

        可以看到返回的是降級后處理的值。

        3.2、Polly熔斷

        熔斷就是當(dāng)一處代碼報錯超過多少次,就讓它熔斷多長時間再恢復(fù),熔斷時Polly會截斷請求,不會再進(jìn)入到具體業(yè)務(wù),這能有效減少沒必要的業(yè)務(wù)性能損耗。

        把OrderService構(gòu)建函數(shù)處改成

        public?OrderService()
        {
        ?????//熔斷
        ?????_policy?=?Policy<string>.Handle()
        ?????.CircuitBreaker(5,?TimeSpan.FromSeconds(10));//連續(xù)出錯5次后熔斷10秒,不會在進(jìn)到業(yè)務(wù)代碼
        }

        測試結(jié)果:

        5次前的返回:

        熔斷后返回:

        3.3、Polly重試

        把OrderService的構(gòu)造函數(shù)處修改為:

        ??public?OrderService()
        ??{
        ??????//重試
        ??????//RetryForever()是一直重試直到成功
        ??????//Retry()是重試最多一次;
        ??????//Retry(n)?是重試最多n次;
        ??????//WaitAndRetry()可以實現(xiàn)“如果出錯等待100ms再試還不行再等150ms秒。
        ???????_policy?=?Policy<string>.Handle()
        ?????????????.WaitAndRetry(new?TimeSpan[]?{?TimeSpan.FromSeconds(5),?TimeSpan.FromSeconds(10),?TimeSpan.FromSeconds(15)?});

        ??}

        這里是業(yè)務(wù)處理失敗時,重試3次,分別隔5s,10s,15s。

        測試結(jié)果:

        可以看到,第一次執(zhí)行因為有異常,然后分別隔5s,10s,15s重試,最后才拋出了異常。

        3.4、Polly超時

        所謂超時,就是我們指定一段代碼的最大運行時間,如果超過這段時間還沒有完成,就直接拋出異常。

        這里判斷超時有兩種策略:一個是悲觀策略(Pessimistic),一個是樂觀策略(Optimistic)。一般我們用悲觀策略。需要注意的是,雖然超時拋除了異常,但這段代碼的運行并沒有停止!

        把OrderService構(gòu)建函數(shù)處改成

        ??public?OrderService()
        ??{
        ????????//超時,業(yè)務(wù)處理超過3秒就直接返回異常
        ????????_policy?=?Policy.Timeout<string>(3,?Polly.Timeout.TimeoutStrategy.Pessimistic);
        ??}

        把OrderService.cs的CreateOrder()方法讓線程睡眠10s

        ?public?string?CreateOrder()
        ?{
        ??????//用polly執(zhí)行
        ??????return?_policy.Execute(()?=>
        ??????{
        ??????????//業(yè)務(wù)邏輯?todo
        ??????????Console.WriteLine($"{DateTime.Now},開始處理業(yè)務(wù)");
        ??????????Thread.Sleep(10000);?//睡眠10s
        ??????????Console.WriteLine("處理完成");
        ??????????return?"成功啦";
        ??????});
        ??}

        執(zhí)行查看結(jié)果:

        可以看到,在3s的時候報了polly的超時異常。

        3.5、Polly組合策略

        上面說的都是單個策略的,其實這些策略是可以組合一起使用的,下面來演示一下。

        把OrderService的構(gòu)造函數(shù)修改為:

        ?public?OrderService()
        ?{
        ?????//重試
        ?????Policy<string>?retry?=?Policy<string>.Handle()
        ???????.WaitAndRetry(new?TimeSpan[]?{?TimeSpan.FromSeconds(5),?TimeSpan.FromSeconds(10),?TimeSpan.FromSeconds(15)?});

        ?????//降級
        ?????Policy<string>?fallback?=?Policy<string>
        ??????????.Handle()?//異常故障
        ??????????.Fallback(()?=>
        ??????????{
        ?????????????//降級回調(diào)
        ?????????????return?"降級后的值";
        ??????????});
        ?????//Wrap:包裹。policyRetry在里面,policyFallback裹在外面。
        ?????//如果里面出現(xiàn)了故障,則把故障拋出來給外面
        ?????//_policy=Policy.Wrap(policy1, policy2, policy3, policy4, policy5);把更多一起封裝。
        ?????_policy?=?Policy.Wrap(fallback,?retry);?//?fallback.Wrap(retry);
        ?}

        把CreateOrder()修改為

        ?public?string?CreateOrder()
        ?{
        ?????//用polly執(zhí)行
        ?????return?_policy.Execute(()?=>
        ?????{
        ?????????//業(yè)務(wù)邏輯?todo
        ?????????Console.WriteLine($"{DateTime.Now},開始處理業(yè)務(wù)");
        ?????????throw?new?Exception("233出錯啦");
        ?????????Console.WriteLine("處理完成");
        ?????????return?"成功啦";
        ?????});
        ?}

        測試結(jié)果:

        重試3次后,返回降級的結(jié)果。

        上面的Ocelot+Polly的熔斷如果去查看Ocelot.Provider.Polly的源碼就會發(fā)現(xiàn)是超時和熔斷的組合實現(xiàn)。

        需要注意的是,組合時Policy.Wrap(fallback, retry);里面的順序也要注意,測試結(jié)果是先執(zhí)行后面的,再執(zhí)行前面的,即前面的把后面的包在內(nèi)層,內(nèi)層執(zhí)行完再拋出給外層處理。

        上面介紹了最常用的策略,Polly也有異步的方法,把上面定義的private Policy_policy; 改成 private AsyncPolicy_policy; 即可。


        轉(zhuǎn)自:包子wxl

        鏈接:cnblogs.com/wei325/archive/2021/09/27/15308498.html

        瀏覽 83
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            成人毛片18女人毛片免费按摩店 | 影音先锋黄色电影 | 日本乱轮视频网站 | 无码激情做a爰片毛片A片孕妇 | 少妇高潮迭起 | 插骚逼网站 | 日韩插穴| 欧美黄片免费观看 | 亚洲视频一区二区 | 国产乱伦自拍视频 |