高可用架構之 Sentinel 的降級原理詳解

當前互聯網處理的業務場景都極爲複雜,各大公司都會根據自己的業務場景搭建微服務來保證單個服務只處理一塊業務,這樣做能極大的提升開發效率,滿足快速迭代的需要,但帶來的問題卻是多個服務下會導致整體服務的可用性下降。

互聯網服務的可用性一般用 SLA(Service Level Agreement 可以翻譯爲服務水平協議)來表示,而我們通常所說的 N 個 9 就是對高可用服務的一個衡量指標。9 越多代表全年服務可用時間越長, 服務會更可靠。

現今互聯網架構裏保證服務的高可用和高穩定性的時候,無非就是熔斷、降級、限流、隔離這幾個策略。今天主要從以下幾個方面來聊聊熔斷降級這塊內容。

高可用服務裏邊爲啥要有熔斷降級

先看下之前遇到過服務調用異常的案例

服務整體的調用關係如下圖:

服務流量監控圖,如下圖:

服務請求耗時,如下圖:

本地服務 B 接收上游服務 A 的請求,並請求到下游服務 C 進行處理後返回給服務 A。

說下上邊兩張圖中的幾個要素:

上圖示服務的調用異常使得本地服務 B 短時間的阻塞,進而導致了本地服務吞吐量急劇下降,嚴重影響服務的可用性和穩定性。經過上述問題之後,我們決定對服務裏邊的依賴進行熔斷降級處理,其目的主要是爲了保護自己的系統。

比如我們依賴的底層服務出現不穩定時,請求的響應時間會變長,調用服務的響應時間也會變長,短時間內的問題可能會導致自身服務的吐吞量降低,即單位時間處理的請求數降低(見上述流量監控圖)。嚴重時會產生線程堆積,極端情況下會耗盡自身應用的線程,從而導致服務不可用。

當我們聊熔斷降級的時候到底在聊什麼

需要強調的是,我們做熔斷降級主要是爲了保護本地服務的可用性,熔斷降級都是在客戶端來做的。因爲當前的微服務都是分佈式的, 調用鏈路非常複雜。當某個單層調用出現問題時,就會產生放大效果,就可能層層級聯,最終導致整個鏈路不可用。降級就是對暫定不穩定的依賴進行切斷狀態,即暫時不去進行調用。

熔斷降級在實際過程中是 2 個步驟:

降級邏輯需要與當前處理場景相結合,並沒有統一的結論。

當前業界比較流行的幾個降級組件

當前業界比較流行的降級組件大概有 Sentinel、Hystrix、Resilience4j,三者區別主要見下:

這裏再多說幾句:

Sentinel 屬於 Alibaba 公司的開源產品, 代碼可讀性相對 Hystrix 友好很多(這個只是個人看法)。而且最重要的是 Sentinel 能支持對於不同異常的處理,比如能區分出正常的業務異常還是需要熔斷的異常。使用者可以選擇在應用層對異常進行處理,比如直接拋出, 這樣業務層可以根據不同的異常選擇不同的處理邏輯, 比如重試等策略。

在隔離策略看來,線程池的隔離策略無疑是優秀的,其原理就是對不同的接口設置不同的線程池,這樣就能與其他接口使用的資源做到基本的隔離,而且能支持異步。但是線程池隔離策略最大的問題就是線程的運行和上下文切換會增加服務器的負載。而信號量隔離策略其原理可以理解爲給接口設置了一個計數器,只能支持這麼多請求的調用,超出該閾值則會觸發 fallback,不能進行異步調用,對服務器負載影響相對較小。

從源碼層面聊聊 Sentinel 的降級功能的實現原理

Sentinel 降級的原理其實並不複雜,熔斷降級原理可以拆解爲降級規則定義,降級數據統計,降級規則驗證,降級實施幾個方面去理解。

其中降級規則的定義包含降級策略,降級參數兩個方面,降級數據統計指的是在指定降級策略下邊對請求或者異常進行計數,降級規則驗證指的是在判斷是否觸發降級,降級實施指的是在熔斷後所採用的降級方案。下來將對這幾塊的內容進行詳述。

降級規則的定義

降級策略

Sentinel 的降級策略見下圖:

需要注意的是,在慢調用策略中,你的請求超過了指定 RT, 此時該請求還會繼續往下執行的,並不會中斷正在執行的請求,只是會在統計時將超出閾值的請求按慢請求計數。

降級參數描述

這塊的 grade 指的是上面描述的三種策略,statIntervalMs 表示的是數據統計時長,timeWindow 表示的是熔斷的時長。觸發熔斷的條件是總請求打到閾值並且請求數必須大於 minRequestAmount(該值默認是 5)。

降級數據統計

先看段 Sentinel 降級的一段代碼,展示如下:

如圖示代碼,在 Sentinel 裏面,所有的資源都對應一個資源名稱(resourceName),每次資源調用都會創建一個 Entry 對象。每個請求過來都會進入 SphU.entry("自定義資源名") 邏輯。當在 entry("自定義資源名") 邏輯裏達到了降級的閾值, 則會拋出 BlockException 進行熔斷而觸發降級。

再來看看 entry("自定義資源名") 裏邊是如何降級的,要觸發降級的前提是要達到規定的閾值,判斷是否達到閾值前提是要進行請求數據統計。Entry 創建的時候,同時也會創建一系列功能插槽(slot chain),這些插槽有不同的職責。

下圖摘自官網:

這些 Slot 的加載是基於 SPI 機制加載的,加載的順序是每個 Slot 上邊有個註解, 來表示加載的順序。

大概說下數據的統計邏輯:

請求計數的入口在 StatisticSlot 類, 負責統計資源的實時狀態,調用到 slotchain 中的任意一個 slot 時,都會觸發該 slot 的 entry 方法。

這塊代碼的邏輯比較複雜,大概邏輯爲:

關於數據統計,主要會牽扯到 ArrayMetric、BucketLeapArray、MetricBucket、WindowWrap 等類。

降級規則驗證

降級規則驗證的入口在 DegradeSlot 類,代碼如下:

在驗證降級規則時,通過 resourceName 取到初始化時加載到內存的降級規則 CircuitBreaker,然後判斷是否通過,沒通過時直接拋出 DegradeException。

判斷當前請求是否通過的代碼如下:

這塊主要是根據 State 的變化來決定是降級還是正常,State 的變化依賴於上邊數據的統計的結果。

降級實施

降級實施這塊其實有兩塊內容:

熔斷降級整個過程中狀態的流轉如下圖:

熔斷後降級的業務處理,簡單理解爲降級後你要執行的方法,也可以快速返回 null。這塊非常要注意的是,你的業務處理一定要考慮你所有的業務場景,比如返回 null 值對上層所有調用方的影響是什麼,一定要搞清楚這個再寫降級邏輯。

總結

Sentinel 的功能非常強大,本文只是針對熔斷降級這塊做了大概的介紹。降級的原理需要結合降級參數在不同的降級策略下的表現,每次請求都會進入當前 entry() 中進行數據統計和降級等操作,需要結合上邊 State 狀態變化的流程來一塊理解。其實熔斷的時候需要基於滑動時間窗口進行數據統計,這塊內容也比較核心,希望改天能單開一篇再給大家詳細進行講解。

來源:

https://www.toutiao.com/article/7008891741885907494/?log_from=90fae59166c7f_1653359151083

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/9TCTG6sCEmg9wFdeKzzw7Q