1. 程式人生 > >三分鐘學會.NET微服務之Polly

三分鐘學會.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));//連續出錯6次之後熔斷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差不多。就這樣了,再見,喜歡點個推薦!