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


 

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

 一.什么是熔斷

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

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

比如我們要做一個雙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)

 官方: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差不多。就這樣了,再見,喜歡點個推薦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM