三分鐘學會.NET微服務之Polly

 

熔斷降級是一個很是重要的概念,咱們先說一下什麼是熔斷降級,我們都知道服務發現,一個有問題的服務器沒來得急註銷過一會就崩潰掉了,那麼咱們的請求就有可能訪問一個已經崩潰的服務器,那麼就會請求失敗,由於已經game over了。那麼這個問題怎麼解決呢,你必定要認可,這個問題是沒法避免的。沒有什麼方法說,我拿到的服務器都沒有問題,這事是不可能的,因此你要認可你會有機會拿到有問題的服務器。那麼熔斷降級就是來解決這種問題的。git

 一.什麼是熔斷

熔斷就像是「保險絲」,當出現夢中情況時,切斷服務,從而防止應用程序不斷地嘗試形成雪崩,這和咱們農村的保險絲很像,天氣熱了防止火災,那保險絲會自動斷開,防止更大的損失。github

降級的目的是當某個服務提供者發生故障的時候,向調用方返回一個錯誤響應替代響應。redis

好比咱們要作一個雙11活動的系統,那麼好比一個抽獎的模塊崩潰,這個時候呢廣大客戶端瘋狂F5,就會致使整個集羣雪崩,這個時候咱們就應該中斷。數據庫

還有一栗子,好比說電信和聯通,它們在穩定,有也不穩定的時候,那麼若是咱們用它們的接口,若是電信崩了,咱們就是用聯通,這種操做的行爲就叫作熔斷降級。緩存

其使用場景呢,例如,咱們要展現商品信息,咱們先從數據庫讀取,讀取失敗了,咱們就經過cache/redis,若是再失敗,咱們經過固定的Javascript object 來綁定,若是再失敗那就返回一個錯誤的信息。這就是完美的下降了錯誤。服務器

這種錯誤的機率就猶如0.01*way³  就是1000次纔會出現的機率。那若是是不熔斷降級 就是100.以上一些栗子,好好讀讀便可。網絡

二.Polly介紹

.Net Core 中有一個被.Net基金會承認的k庫,能夠用來進行熔斷降級,主要功能:1.重試(retry);2.斷路器(circuit-breaker);3.超時檢測(timeout);4.緩存(cache);5.降級(fallback)app

 官方:https://github.com/app-vnext/polly  nuget: install-package Polly-Version 6.0.1框架

三.使用

建立項目與安裝庫,爲了穩定仍是選擇6.0.1吧。異步

使用Policy的靜態方法建立ISyncPolicy實現類對象,建立方法j既有同步方法也有異步方法,根據本身的需求來選擇,下面先演示同步方法,異步的方法也相似。

static void Main(string[] args)
        {
            //handle 當發生argumentException的異常
            Policy policy = Policy.Handle<ArgumentException>() .Fallback(() => { //就幹什麼事情 Console.WriteLine("出錯了"); });
       //有可能異常的時候 policy.Execute(() => { Console.WriteLine("開始執行"); throw new ArgumentException(); Console.WriteLine("執行結束"); }); }

咱們運行一下,是以下結果。

但若是書咱們故意寫一個拋出異常,咱們稍微修改一下代碼。

policy.Execute(() =>
            {
                Console.WriteLine("開始執行"); throw new ArgumentException(); Console.WriteLine("執行結束"); });

上面呢,我麼捕捉的是ArgumentException異常,那麼咱們若是是報的其餘的錯誤應該會怎樣呢?

policy.Execute(() =>
            {
                Console.WriteLine("開始執行"); throw new Exception(); Console.WriteLine("執行結束"); });

 FallBack中有不少不一樣的重載,咱們能夠根據重載獲取不一樣的報錯信息,如下是全部的方法。

咱們能夠簡單的去獲取一個對象,代碼以下:

 Policy policy = Policy.Handle<ArgumentException>()
                .Fallback(() => { //就幹什麼事情 Console.WriteLine("出錯了"); },ex=> { Console.WriteLine(ex.Message); }); policy.Execute(() => { Console.WriteLine("開始執行"); throw new ArgumentException(); Console.WriteLine("執行結束"); });

由於我們的業務邏輯啊有多是帶返回值的,也有多是不帶返回值的。那若是你的業務邏輯是帶返回值的,你就得用一個Policy帶參的泛型來建立,那麼這個Policy也是泛型的,在FallBack中也要 提一個替代值,由於畢竟是降級嘛,確定要有一個值來進行替代。

Policy<string> policy = Policy<string>.Handle<Exception>()
                .Fallback(() => { return "降級後的值"; }); string value = policy.Execute(() => { return "正常值"; });

四.重試處理

polly提供了重試處理機制,那麼這個RetryForever()的場景不可能會出現,我也不知道它是處於什麼個操做。我以爲這違背了熔斷降級,這不可能讓它重試的,那麼如下就是用法,可是這根本用不着~

仍是推薦使用Retry吧。Retry中能夠寫個int值進去,就是重試的次數,這個仍是不錯的!

Policy policy = Policy.Handle<Exception>().RetryForever();
            policy.Execute(() => { Console.WriteLine("play task!!"); if (DateTime.Now.Second % 10 != 0) { throw new Exception(); } Console.WriteLine("完成任務!"); });

不過仍是有比較正常點的方法,例如WaitAndRetry這個方法,等等再重試。這個很不錯!這個方法裏的重載很是之多。

Policy policy = Policy.Handle<Exception>().WaitAndRetry(100, i => TimeSpan.FromMinutes(100));
            policy.Execute(() => { Console.WriteLine("play task!!"); if (DateTime.Now.Second % 10 != 0) { throw new Exception(); } Console.WriteLine("完成任務!"); });

 五.短路保護Circuit Breaker

短路保護是什麼意思呢,這個單詞翻譯過來就叫作線路切斷器,出現N次連續錯誤,則把「熔斷器」(保險絲)熔斷,等待一段時間,等待這段時間內若是再Execute則直接拋出BrokenCircuitException異常,根本不會再去嘗試調用業務代碼。等待時間過去以後,再執行Execute的時候若是又錯了(一次就夠了),那麼繼續熔斷一段時間,不然就恢復正常。這樣就避免一個服務已經不可用了,仍是使勁的請求給系統形成更大壓力。

這樣就避免了一個服務不可用了還在使勁的請求。

 Policy policy = Policy
            .Handle<Exception>() .CircuitBreaker(3, TimeSpan.FromSeconds(5));//連續出錯3次以後熔斷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); }

 這就像剛纔咱們說的,我設置了連續3次熔斷,那麼若是連續3次報錯,那麼直接再也不執行之後的內容,這無疑是很是不錯的機制。保證了服務器的性能丟失和不起眼的問題。

 六.策略封裝與超時處理

策略封裝使用的方法是Policy提供的Wrap方法,英譯叫作包裹,那麼從單詞的意思就知道,能夠經過策略包裹策略來進行封裝,即裏面的不行,就走外面的。

Policy policyRetry = Policy.Handle<Exception>()
                .Retry(3); Policy policyFallback = Policy.Handle<Exception>() .Fallback(() => { Console.WriteLine("降級"); }); Policy policy = policyFallback.Wrap(policyRetry); policy.Execute(() => { Console.WriteLine("play task!!"); if (DateTime.Now.Second % 10 != 0) { throw new Exception(); } Console.WriteLine("完成任務!"); });

注意這個wrap的包裹順序的,外在後,內在前。再經過一個超時處理就能夠對消耗時間夠長的請求進行GG了。

那麼你就能夠經過超時處理來對咱們文章開頭的訴說進行一個很是生動形象的經過代碼來宣誓。下面說明超時異常的說明

Policy policytimeout = Policy.Timeout(3, TimeoutStrategy.Pessimistic);
            Policy policyFallBack = Policy.Handle<TimeoutRejectedException>() .Fallback(() => { Console.WriteLine("熔斷降級"); }); Policy policy = policyFallBack.Wrap(policytimeout); policy.Execute(() => { Console.WriteLine("完成任務"); Thread.Sleep(5000); Console.WriteLine("完成任務"); }); Console.ReadKey();

這玩膩的用途不過就是:請求網絡接口,避免接口長期沒有響應形成系統卡死。

 七.Polly的異步

     Test1().Wait(); //調用

        static async Task Test1() { Policy<byte[]> policy = Policy<byte[]> .Handle<Exception>() .FallbackAsync(async c => { Console.WriteLine("執行出錯"); return new byte[0]; }, async r => { Console.WriteLine(r.Exception); }); policy = policy.WrapAsync(Policy.TimeoutAsync(20, 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("https://www.cnblogs.com/images/logo_small.gif"); Console.WriteLine("完成任務"); return result; }); Console.WriteLine("bytes長度" + bytes.Length); }

使用Polly的異步,那麼全部的方法都必須是異步,除了Handle方法,由於handle就不須要異步,也沒有返回值。經過異步呢,全部的重載方法都構造了一遍,仍是能夠繼續用的。那麼這段代碼的意思是,經過異步的方式若是我經過httpclient獲取某站點的圖片的base值,若是在此期間我定義了一個policy,抓住一個異常,若是說兩秒以內尚未反應我就超時。直接終止。測試的時候 你能夠把值 改變下。

八.最後

相信你跟着我寫到如今,已經感到這代碼已經很是噁心了,代碼中有很是多冗餘的代碼,近期會使用AspectCore這個AOP框架,聽別人說這個庫不錯,這和Spring cloud的Hystrix差很少。就這樣了,再見,喜歡點個推薦!

相關文章
相關標籤/搜索