熔斷和降級的初步詳解實現(NET Core控制檯輸出講解Polly)

學習中的苦與樂發表於2021-12-15

概述

很多朋友包括我,對於“八股文”可以說是比較熟練的,每次面試前都會專研不少東西,各種固定答案、專業術語都是張口就來,一個字,穩。

八股文:程式設計師八股文是指程式設計師在面試過程中經常被問到的問題,大多都有固定化、格式化的答案,俗稱為面經。

八股文是明清科舉考試的一種文體,也稱制義、制藝、時文、八比文。

八股文就是指文章的八個部分,文體有固定格式:由破題、承題、起講、入題、起股、中股、後股、束股八部分組成,題目一律出自四書五經中的原文。

後四個部分每部分有兩股排比對偶的文字,合起來共八股。

相信大家對熔斷、降級、隔離、限流、容災備份、故障轉移、異地多活等專業術語不陌生吧,或多或少都能聊上一兩句。

但是在實際開發中卻望著鍵盤發呆,不知道怎麼實現,今天我們就簡單演示一下再專案當中怎麼實現熔斷、降級。

實現的方式有很多,我們這裡講解Polly,但是大家不要過分依賴,可以多種方式進行預演防範。

強如淘寶,在今年(2021年10月20號)20點10分左右都崩了,20點27分左右修復。

熔斷和降級概念理解

熔斷機制概念

當下遊服務因訪問壓力過大而響應變慢或失敗,或者下游服務出現異常時,上游服務為了保護系統整體的可用性,可以暫時切斷對下游服務的呼叫。

PS:如果專案中不使用熔斷將會影響到整個系統的使用。

 

舉例:

家庭裡有很多大功率電器(微波爐,電磁爐,電炒鍋,空調,飲水機,電熱水器、浴霸、太陽能抽水電機等),

一旦我們全部開啟大功能電器,就會造成超負荷執行引起跳閘停電,導致家裡面全面停電,這就不符合用電目的,我們想哪個大功能超負荷就停哪個,不能因為區域性影響全域性。

這個時候我們需要在家庭總電閘下面加裝二級電閘、三級電閘.....,達到一機一閘一保護的目的(也可以多個電器安裝一個電閘)。

如果在這個例子中,把用電換成我們程式中的介面(介面能承受3000人同時訪問),然後把大功率電器替換成我們的介面呼叫者。

當介面的併發超過3000這個點的時候介面將會掛掉,導致伺服器崩潰,作為呼叫者並不能控制這個點。

為了防止我們的介面發生崩潰的情況,我們做一個報警的設定,當發生這個情況的時候暫時切斷服務,防止服務崩潰(使用者量到達3000的時候,拒絕其他請求,只服務這3000以內的請求)。

降級機制概念

降級是當某個服務出現故障啟動熔斷機制後,向呼叫方返回一個替代響應或者規範化的錯誤響應。

舉例:

我們的程式需要傳送郵件,首先呼叫自建郵箱傳送,當自建郵箱傳送失敗後呼叫網易郵箱傳送郵件,如果網易郵箱傳送失敗,那麼我們就呼叫 QQ 郵箱傳送,如果 QQ 郵箱也傳送失敗,那麼我們就返回傳送失敗響應。
降級也可以看作服務的選擇性放棄。
例如1:上面的多個大功能電器不需要同時執行時,我們可以關閉其他電器,等待當前電器用完後在使用。
例如2:李佳琪和薇婭的直播帶貨時,平臺流量優先傾斜,導致其他主播的直播間卡慢不清晰等問題,等待李佳琪薇婭直播結束後其他主播恢復正常。
例如3:李佳琪和薇婭的直播帶貨時,平臺流量優先傾斜,導致其他主播的直播間卡慢不清晰等問題,等待李佳琪薇婭直播結束後其他主播恢復正常。

當服務介面發生錯誤的時候,可以找到一個替代方法進行使用。這個方法可能是啟用另一臺伺服器的介面,也可能是隻返回“伺服器繁忙請重試的提示”。總之不會把一個系統級別的問題暴露給使用者。

什麼是 Polly

暫時我所瞭解的處理熔斷降級的框架有NetFlix的Hystrix框架和Polly框架。我們這裡使用Polly。

Polly 是被 .NET 基金會認可的彈性和瞬態故障處理庫,允許開發人員以流暢和執行緒安全的方式表達策略,如重試、斷路器、超時、艙壁隔離和回退。

Polly 的7種策略 [1]:重試、斷路(熔斷)、降級、超時、隔離、快取以及策略包集合 Polly 的核心是動作和故障,其中動作主要包含降級、熔斷和重試等,策略則包含用來執行的程式碼。

1、重試(Retry):

  當程式發生短暫的故障、並且故障在延遲後,可以自動糾正的,前期是暫時的我們可以配置自動重試。

2、斷路器(Circuit-Breaker):

  當一個系統陷入嚴重的問題時,讓系統快速的失敗,比讓使用者一直等待著要好的多,保護一個系統不受過載的影響,可以幫助它快速的恢復。

3、超時(Timeout):

  在等待一定的時間後,沒有返回相應的結果,保證程式不會一直等待下去,使呼叫者不必一直的等待下去。

4、隔離(Bulkhead Isolation):

  當程式出現故障的時,一臺主機中的多個失敗的程式,對資源(例如執行緒/CPU)的一直佔用,當下遊的系統發生故障的時候,也可能導致上游對資源的呼叫失敗、這兩種風險的出現都將導致更大範圍的影響、隔離策略是為了防止“一個點的失敗導致整盤的失敗”把受到管理的操作固定在某個資源中,避免影響到其他的資源。

5、快取(Cache):

  針對相同的請求,在第一次訪問的時候將響應的資料進行快取,再次訪問的時候直接在快取中提供響應的資料。

6、回退(FallBack):

  當程式發生失敗的情況的時候,我們將做些什麼,定義一個在程式發生失敗的時候要執行的動作。

7、策略組合(PolicyWrap):

  Polly針對不同的故障有不同的策略,我們可以靈活的組合策略,上述的六種策略可以靈活組合使用。

Polly的基本用法

我們建立一個.NET Core 2.1 控制檯應用程式,命名為 PollyDemo。

在Nuget中引用第三方庫Polly,選擇最新穩定版。

1、重試策略(Retry)

我們定義一個 GetUser 方法,這個方法用來模擬網路請求並在方法結尾製造了一個 Http 異常。

在 Main 方法中我們設定了重試次數 5 次,並且規定了重試過程中要執行的業務邏輯,最後呼叫 Execute 方法指定針對哪段程式碼執行這個策略。

我們執行程式碼可以發現當觸發異常後,程式不斷的重試呼叫指定的程式碼段,直到達到重試次數後不再重試並丟擲異常。

我們在使用重試策略需要注意的是,當達到重試次數後程式碼會丟擲異常,因此我們必須在將充實策略程式碼包裹在 try_catch 程式碼段中。

using Polly;
using System;
using System.Net.Http;
using System.Threading;

namespace PollyDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //如果程式發生錯誤將重新執行 5 次
                Policy.Handle<HttpRequestException>().Retry(5, ((exception, count, context) =>
                {
                    Console.WriteLine($"執行失敗,第 {count} 次重試");
                })).Execute(GetUser);
            }
            catch (Exception e)
            {
                Console.WriteLine("異常丟擲:"+e.Message);
            }
            Console.Read();
        }

        //開始執行程式碼塊
        static void GetUser()
        {
            Console.WriteLine("開始執行");
            Thread.Sleep(1000);
            throw new HttpRequestException();  //直接定義錯誤丟擲
        }
    }
}

 

執行結果

 

2、斷路器(Circuit-Breaker)

 

程式首次被訪問如果連續出現三次錯誤後,將暫停3秒,三秒結束後 可以被繼續訪問,

繼續訪問時 如果第一次就出現錯誤 ,繼續熔斷10次。

using Polly;
using System;
using System.Net.Http;
using System.Threading;

namespace PollyDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //連續訪問3ci
            var PolicyExecutes =
                Policy<object>.Handle<Exception>()
                .CircuitBreaker(3, TimeSpan.FromSeconds(3));

            //模擬訪問10次
            for (int i = 0; i < 10; i++)
            {
                try
                {
                    PolicyExecutes.Execute(() =>
                    {
                        Console.WriteLine("********************開始執行程式**************************");
                        throw new Exception("異常");
                    });
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Exception資訊{i}" + ex.Message);
                }
                //一秒執行一次
                Thread.Sleep(1000);
            }
            Console.Read();

        }
    }
}

執行結果

 

3、超時策略(Timeout)

程式本身沒有錯誤就是執行的時間超過了我們制定的超時策略的時間然後程式就抓捕到超時的異常。

using Polly;
using Polly.Timeout;
using System;
using System.Net.Http;
using System.Threading;

namespace PollyDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                //超時策略(程式沒錯,只是執行時間超過設定的時間)
                Policy policy = Policy.Timeout(3, TimeoutStrategy.Pessimistic);

                policy.Execute(() =>
                {
                    Console.WriteLine("********************開始執行程式**************************");
                    Console.WriteLine("等待執行中...");
                    Thread.Sleep(5000); //設定5s超時
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine("執行超時,請重試");
            }
            Console.Read();

        }
    }
}

執行過程

 

4、隔離策略(Bulkhead Isolation)

制定策略最大的併發數為5

using Polly;
using Polly.Timeout;
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace PollyDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Policy PolicyExecutes = Policy.Bulkhead(5);
            //程式執行6次 總會有一個失敗的,但是這個失敗的不會影響其他5個的執行結果
            for (int i = 0; i < 6; i++)
            {
                Task.Factory.StartNew(() =>
                {
                    try
                    {
                        PolicyExecutes.Execute(() =>
                        {
                            Console.WriteLine($"********************開始執行程式**************************");
                            Thread.Sleep(5000); //為了看出效果,設定5s停頓,便於併發執行錯誤
                        });
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                });
            }
            Console.Read();

        }
    }
}

執行結果

 

5、快取策略(Cache)

在開發時我們會把頻繁使用並且很少變化的資源快取起來,來提高系統的效能。

Polly 提供了強大且易於上手的快取策略的支援,使得我們不用再手動編寫快取策略。

這裡我不提供示例程式碼,一來我暫時還沒使用這個策略,二來這一小部分需要根據不同專案和不同公司的框架來訂。

using System;

namespace PollyDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //Policy.Cache 可以指定 過期時間,絕對過期時間,滑動過期時間等
            //Policy 的快取策略的制定
            MemoryCache memoryCache = null;
            var PolicyExecutes = Policy.Cache(memoryCache, TimeSpan.FromMinutes(5));
            Console.Read();
        }
    }
}

6、回退(FallBack)

當主程式發生錯誤是我們啟動備用的程式進行程式處理。

using Polly;
using System;

namespace PollyDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var PolicyExecute = Policy.Handle<Exception>().Fallback(() =>
            {
                Console.WriteLine($"你的程式報錯了,我是替代程式!");

            });

            //執行
            PolicyExecute.Execute(() =>
            {
                Console.WriteLine("開始執行程式");
                Console.WriteLine("");
                throw new Exception();
            });
            Console.Read();
        }
    }
}

執行結果

 

7、策略組合(PolicyWrap)

 PolicyWrap的上面六種策略可以任意的組合起來使用:我們將超時策略(Timeout)加上回退(FallBack)策略組合使用。

using Polly;
using Polly.Timeout;
using System;
using System.Threading;

namespace PollyDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Policy PolicyExecute = Policy.Handle<Exception>().Fallback(() =>
            {
                Console.WriteLine("你的執行程式超時了,我是替代程式!");

            });
            //超時策略
            PolicyExecute = PolicyExecute.Wrap(Policy.Timeout(3, TimeoutStrategy.Pessimistic));
            //執行
            PolicyExecute.Execute(() =>
            {
                Console.WriteLine($"我是一開始的執行程式");
                Console.WriteLine("");
                Thread.Sleep(5000);
            });
            Console.Read();
        }
    }
}

執行結果

 

總結

本文開頭主要介紹了熔斷和降級的相關概念,然後用講解了 Polly 的其中策略,這些都可以作為掌握 Polly 的基礎。

因為 Polly 是一個強大而豐富的 .NET 第三方庫,無法通過一篇文章具體而又詳細的講解出來,因此對於 Polly 和 熔斷以及降級有興趣的同學可以上 Polly 官方文件中具體學習。

參考文獻

1.Polly的7種策略:https://blog.csdn.net/sd7o95o/article/details/117003880

2.使用.NetCore 控制檯演示 熔斷 降級(polly)

 

 
歡迎關注訂閱微信公眾號【熊澤有話說】,更多好玩易學知識等你來取
作者:熊澤-學習中的苦與樂
公眾號:熊澤有話說
出處: https://www.cnblogs.com/xiongze520/p/15693245.html
您可以隨意轉載、摘錄,但請在文章內註明作者和原文連結。  

 

 

 

相關文章