<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 9188字,需瀏覽 19分鐘

           ·

          2021-10-26 17:46


          一、簡(jiǎn)介

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

          瞬態(tài)故障就是可能會(huì)出現(xiàn)的,比喻網(wǎng)絡(luò)不穩(wěn)定或無(wú)法訪問(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(同一個(gè)地址請(qǐng)求就返回緩存結(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),每次請(qǐng)求Consul
          ??????}
          ????}
          ??}

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

          測(cè)試:訪問(wèn) http://172.16.2.9:5200/T1/Test/GetName

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

          10s后刷新端口變成5202

          2.2、Ocelot限流

          為什么要限流呢,防止請(qǐng)求過(guò)多把程序搞宕機(jī)了,也可以有效防止爬蟲和ddos攻擊,預(yù)估出服務(wù)的處理能力,然后設(shè)置限流,可以限制單位時(shí)間內(nèi)的訪問(wèn)量(失敗一部分請(qǐng)求比整個(gè)服務(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)計(jì)時(shí)間段:1s, 5m, 1h, 1d
          ??????????"PeriodTimespan":?10,?//?多少秒之后客戶端可以重試
          ??????????"Limit":?5?//?在統(tǒng)計(jì)時(shí)間段內(nèi)允許的最大請(qǐng)求數(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),每次請(qǐng)求Consul
          ??????},
          ??????//限流
          ??????"RateLimitOptions":?{
          ????????"QuotaExceededMessage":?"errr:request?fast!",?//限流后響應(yīng)內(nèi)容
          ????????"HttpStatusCode":?666,?//http狀態(tài)碼可以自定義?????"ClientIdHeader":?"client_id"?//?用來(lái)識(shí)別客戶端的請(qǐng)求頭,默認(rèn)是?ClientId
          ??????}
          ????}
          ??}

          這里用Postman來(lái)演示比較直觀。

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

          不受限流影響,可以無(wú)限訪問(wèn)。

          2.3、Ocelot+Polly的熔斷

          安裝NuGet包 Ocelot.Provider.Polly。

          Startup.cs增加 .AddPolly()

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

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

          接口超過(guò)多長(zhǎng)時(shí)間進(jìn)入半熔斷狀態(tài),返回服務(wù)不可用, 連續(xù)超過(guò)多少次進(jìn)入熔斷狀態(tài)就直接停掉該請(qǐng)求返回,多長(zhǎng)時(shí)間再恢復(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,?//允許多少個(gè)異常請(qǐng)求
          ??????????"DurationOfBreak":?5000,?//?熔斷的時(shí)間5s,單位為ms
          ??????????"TimeoutValue":?5000?//單位ms,如果下游請(qǐng)求的處理時(shí)間超過(guò)多少則自如將請(qǐng)求設(shè)置為超時(shí)?默認(rèn)90秒
          ????????}
          ??????}
          ????],
          ????"GlobalConfiguration":?{
          ??????//Ocelot應(yīng)用對(duì)外地址
          ??????"BaseUrl":?"http://172.16.2.9:5200",
          ??????"ServiceDiscoveryProvider":?{
          ????????//Consul地址
          ????????"Host":?"172.16.2.84",
          ????????//Consul端口
          ????????"Port":?8500,
          ????????"Type":?"Consul"?//由Consul提供服務(wù)發(fā)現(xiàn),每次請(qǐng)求Consul
          ??????}
          ????}
          ??}

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

          ?[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后返回");
          ?????}
          ?}

          訪問(wèn)GetSleep()接口,前三次等待6s返回503,后面訪問(wèn)什么接口都是快速返回503,服務(wù)熔斷。

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

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

          3.1、Polly降級(jí)

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

          現(xiàn)在在之前的三個(gè)服務(wù)中加入Polly降級(jí)策略

          安裝NuGet包 --Polly

          新建一個(gè)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);
          ????????}
          ????}

          新建一個(gè)OrderService.cs

          ??public?class?OrderService
          ??{
          ??????private?Policy<string>?_policy;
          ??????public?OrderService()
          ??????{
          ??????????//降級(jí)
          ??????????_policy?=?Policy<string>
          ??????????????.Handle()?//異常故障
          ??????????????.Fallback(()?=>
          ??????????????{
          ??????????????????//降級(jí)回調(diào)?todo降級(jí)后邏輯
          ??????????????????return?"降級(jí)后的值";
          ??????????????});

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

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

          把OrderService注入IOC容器,注意,使用Polly時(shí),實(shí)例一定要以單例模式注入,因?yàn)槿绻敲看味紙?zhí)行構(gòu)造函數(shù)給_policy賦一次值,那_policy每次都是全新的,下面的熔斷策畋會(huì)不生效。

          Startup.cs中的ConfigureServices()加上

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

          測(cè)試,訪問(wèn)http://ip:端口/api/Order/CreateOrder

          可以看到返回的是降級(jí)后處理的值。

          3.2、Polly熔斷

          熔斷就是當(dāng)一處代碼報(bào)錯(cuò)超過(guò)多少次,就讓它熔斷多長(zhǎng)時(shí)間再恢復(fù),熔斷時(shí)Polly會(huì)截?cái)嗾?qǐng)求,不會(huì)再進(jìn)入到具體業(yè)務(wù),這能有效減少?zèng)]必要的業(yè)務(wù)性能損耗。

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

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

          測(cè)試結(jié)果:

          5次前的返回:

          熔斷后返回:

          3.3、Polly重試

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

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

          ??}

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

          測(cè)試結(jié)果:

          可以看到,第一次執(zhí)行因?yàn)橛挟惓?,然后分別隔5s,10s,15s重試,最后才拋出了異常。

          3.4、Polly超時(shí)

          所謂超時(shí),就是我們指定一段代碼的最大運(yùn)行時(shí)間,如果超過(guò)這段時(shí)間還沒(méi)有完成,就直接拋出異常。

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

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

          ??public?OrderService()
          ??{
          ????????//超時(shí),業(yè)務(wù)處理超過(guò)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},開(kāi)始處理業(yè)務(wù)");
          ??????????Thread.Sleep(10000);?//睡眠10s
          ??????????Console.WriteLine("處理完成");
          ??????????return?"成功啦";
          ??????});
          ??}

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

          可以看到,在3s的時(shí)候報(bào)了polly的超時(shí)異常。

          3.5、Polly組合策略

          上面說(shuō)的都是單個(gè)策略的,其實(shí)這些策略是可以組合一起使用的,下面來(lái)演示一下。

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

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

          ?????//降級(jí)
          ?????Policy<string>?fallback?=?Policy<string>
          ??????????.Handle()?//異常故障
          ??????????.Fallback(()?=>
          ??????????{
          ?????????????//降級(jí)回調(diào)
          ?????????????return?"降級(jí)后的值";
          ??????????});
          ?????//Wrap:包裹。policyRetry在里面,policyFallback裹在外面。
          ?????//如果里面出現(xiàn)了故障,則把故障拋出來(lái)給外面
          ?????//_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},開(kāi)始處理業(yè)務(wù)");
          ?????????throw?new?Exception("233出錯(cuò)啦");
          ?????????Console.WriteLine("處理完成");
          ?????????return?"成功啦";
          ?????});
          ?}

          測(cè)試結(jié)果:

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

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

          需要注意的是,組合時(shí)Policy.Wrap(fallback, retry);里面的順序也要注意,測(cè)試結(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
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级欧美性爱视频 | 极品人妻在线 | 免费的欧美黄色高清视频网站 | 欧美AAA性爱 | jizz美女 |