Kitex 熔斷器

2022-04-26 15:26 更新

熔斷器

Kitex 提供了熔斷器的實現(xiàn),但是沒有默認開啟,需要用戶主動使用。

下面簡單介紹一下如何使用以及 Kitex 熔斷器的策略。

如何使用

使用示例:

// build a new CBSuite
cbs := circuitbreak.NewCBSuite(GenServiceCBKeyFunc)

// add to the client options
opts = append(opts, client.WithCircuitBreaker(cbs))

// init client
cli, err := xxxservice.NewClient(targetService, opts)

使用說明

Kitex 大部分服務(wù)治理模塊都是通過 middleware 集成,熔斷也是一樣。Kitex 提供了一套 CBSuite,封裝了服務(wù)粒度的熔斷器和實例粒度的熔斷器。

- 服務(wù)粒度熔斷:

  按照服務(wù)粒度進行熔斷統(tǒng)計,通過 WithMiddleware 添加。服務(wù)粒度的具體劃分取決于 Circuit Breaker Key,既熔斷統(tǒng)計的 key,初始化 CBSuite 時需要傳入 **GenServiceCBKeyFunc**,默認提供的是 circuitbreaker.RPCInfo2Key ,該 key 的格式是 `fromServiceName/toServiceName/method`,即按照方法級別的異常做熔斷統(tǒng)計。

- 實例粒度熔斷

  按照實例粒度進行熔斷統(tǒng)計,主要用于解決單實例異常問題,如果觸發(fā)了實例級別熔斷,框架會自動重試。

  注意,框架自動重試的前提是需要通過 **WithInstanceMW** 添加,WithInstanceMW 添加的 middleware 會在負載均衡后執(zhí)行。
  • 熔斷閾值及閾值變更

默認的熔斷閾值是 ErrRate: 0.5, MinSample: 200,錯誤率達到 50% 觸發(fā)熔斷,同時要求統(tǒng)計量 >200。若要調(diào)整閾值,調(diào)用 CBSuite 的 UpdateServiceCBConfig 和 UpdateInstanceCBConfig 來更新 Key 的閾值。

熔斷器作用

在進行 RPC 調(diào)用時,下游服務(wù)難免會出錯;

當下游出現(xiàn)問題時,如果上游繼續(xù)對其進行調(diào)用,既妨礙了下游的恢復(fù),也浪費了上游的資源;

為了解決這個問題,你可以設(shè)置一些動態(tài)開關(guān),當下游出錯時,手動的關(guān)閉對下游的調(diào)用;

然而更好的辦法是使用熔斷器,自動化的解決這個問題。

這里是一篇更詳細的熔斷器介紹。

比較出名的熔斷器當屬 hystrix 了,這里是它的設(shè)計文檔。

熔斷策略

熔斷器的思路很簡單:根據(jù) RPC 的成功失敗情況,限制對下游的訪問;

通常熔斷器分為三個時期: CLOSED、OPEN、HALFOPEN;

RPC 正常時,為 CLOSED;

當 RPC 錯誤增多時,熔斷器會被觸發(fā),進入 OPEN;

OPEN 后經(jīng)過一定的冷卻時間,熔斷器變?yōu)?nbsp;HALFOPEN;

HALFOPEN 時會對下游進行一些有策略的訪問,然后根據(jù)結(jié)果決定是變?yōu)?nbsp;CLOSED,還是 OPEN;

總的來說三個狀態(tài)的轉(zhuǎn)換大致如下圖:

[CLOSED] ---> tripped ----> [OPEN]<-------+
    ^                          |           ^
    |                          v           |
    +                          |      detect fail
    |                          |           |
    |                    cooling timeout   |
    ^                          |           ^
    |                          v           |
    +--- detect succeed --<-[HALFOPEN]-->--+

觸發(fā)策略

Kitex 默認提供了三個基本的熔斷觸發(fā)策略:

  • 連續(xù)錯誤數(shù)達到閾值 (ConsecutiveTripFunc)
  • 錯誤數(shù)達到閾值 (ThresholdTripFunc)
  • 錯誤率達到閾值 (RateTripFunc)

當然,你可以通過實現(xiàn) TripFunc 函數(shù)來寫自己的熔斷觸發(fā)策略;

Circuitbreaker 會在每次 Fail 或者 Timeout 時,去調(diào)用 TripFunc,來決定是否觸發(fā)熔斷;

冷卻策略

進入 OPEN 狀態(tài)后,熔斷器會冷卻一段時間,默認是 10 秒,當然該參數(shù)可配置 (CoolingTimeout);

在這段時期內(nèi),所有的 IsAllowed() 請求將會被返回 false;

冷卻完畢后進入 HALFOPEN;

半打開時策略

在 HALFOPEN 時,熔斷器每隔 " 一段時間 " 便會放過一個請求,當連續(xù)成功 " 若干數(shù)目 " 的請求后,熔斷器將變?yōu)?nbsp;CLOSED; 如果其中有任意一個失敗,則將變?yōu)?nbsp;OPEN;

該過程是一個逐漸試探下游,并打開的過程;

上述的 " 一段時間 “(DetectTimeout) 和 " 若干數(shù)目 “(DEFAULT_HALFOPEN_SUCCESSES) 都是可以配置的;

統(tǒng)計

默認參數(shù)

熔斷器會統(tǒng)計一段時間窗口內(nèi)的成功,失敗和超時,默認窗口大小是 10S;

時間窗口可以通過兩個參數(shù)設(shè)置,不過通常情況下你可以不用關(guān)心 .

統(tǒng)計方法

統(tǒng)計方法是將該段時間窗口分為若干個桶,每個桶記錄一定固定時長內(nèi)的數(shù)據(jù);

比如統(tǒng)計 10 秒內(nèi)的數(shù)據(jù),于是可以將 10 秒的時間段分散到 100 個桶,每個桶統(tǒng)計 100ms 時間段內(nèi)的數(shù)據(jù);

Options 中的 BucketTime 和 BucketNums,就分別對應(yīng)了每個桶維護的時間段,和桶的個數(shù);

如將 BucketTime 設(shè)置為 100ms,將 BucketNums 設(shè)置為 100,則對應(yīng)了 10 秒的時間窗口;

抖動 

隨著時間的移動,窗口內(nèi)最老的那個桶會過期,當最后那個桶過期時,則會出現(xiàn)了抖動;

舉個例子:

  • 你將 10 秒分為了 10 個桶,0 號桶對應(yīng)了 [0S,1S) 的時間,1 號桶對應(yīng) [1S,2S),…,9 號桶對應(yīng) [9S,10S);
  • 在 10.1S 時,執(zhí)行一次 Succ,則 circuitbreaker 內(nèi)會發(fā)生下述的操作;
    • (1) 檢測到 0 號桶已經(jīng)過期,將其丟棄;
    • (2) 創(chuàng)建新的 10 號桶,對應(yīng) [10S,11S);
    • (3) 將該次 Succ 放入 10 號桶內(nèi);
  • 在 10.2S 時,你執(zhí)行 Successes() 查詢窗口內(nèi)成功數(shù),則你得到的實際統(tǒng)計值是 [1S,10.2S) 的數(shù)據(jù),而不是 [0.2S,10.2S);

如果使用分桶計數(shù)的辦法,這樣的抖動是無法避免的,比較折中的一個辦法是將桶的個數(shù)增多,可以降低抖動的影響;

如劃分 2000 個桶,則抖動對整體的數(shù)據(jù)的影響最多也就 1/2000; 在該包中,默認的桶個數(shù)也是 2000,桶時間為 5ms,總體窗口為 10S;

當時曾想過多種技術(shù)辦法來避免這種問題,但是都會引入更多其他的問題,如果你有好的思路,請 issue 或者 PR.


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號