Kitex 熔斷器

2022-04-26 15:26 更新

熔斷器

Kitex 提供了熔斷器的實(shí)現(xiàn),但是沒(méi)有默認(rèn)開(kāi)啟,需要用戶主動(dòng)使用。

下面簡(jiǎ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)

使用說(shuō)明

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

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

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

- 實(shí)例粒度熔斷

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

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

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

熔斷器作用

在進(jìn)行 RPC 調(diào)用時(shí),下游服務(wù)難免會(huì)出錯(cuò);

當(dāng)下游出現(xiàn)問(wèn)題時(shí),如果上游繼續(xù)對(duì)其進(jìn)行調(diào)用,既妨礙了下游的恢復(fù),也浪費(fèi)了上游的資源;

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

然而更好的辦法是使用熔斷器,自動(dòng)化的解決這個(gè)問(wèn)題。

這里是一篇更詳細(xì)的熔斷器介紹。

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

熔斷策略

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

通常熔斷器分為三個(gè)時(shí)期: CLOSED、OPEN、HALFOPEN;

RPC 正常時(shí),為 CLOSED;

當(dāng) RPC 錯(cuò)誤增多時(shí),熔斷器會(huì)被觸發(fā),進(jìn)入 OPEN;

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

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

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

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

觸發(fā)策略

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

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

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

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

冷卻策略

進(jìn)入 OPEN 狀態(tài)后,熔斷器會(huì)冷卻一段時(shí)間,默認(rèn)是 10 秒,當(dāng)然該參數(shù)可配置 (CoolingTimeout);

在這段時(shí)期內(nèi),所有的 IsAllowed() 請(qǐng)求將會(huì)被返回 false;

冷卻完畢后進(jìn)入 HALFOPEN;

半打開(kāi)時(shí)策略

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

該過(guò)程是一個(gè)逐漸試探下游,并打開(kāi)的過(guò)程;

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

統(tǒng)計(jì)

默認(rèn)參數(shù)

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

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

統(tǒng)計(jì)方法

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

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

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

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

抖動(dòng) 

隨著時(shí)間的移動(dòng),窗口內(nèi)最老的那個(gè)桶會(huì)過(guò)期,當(dāng)最后那個(gè)桶過(guò)期時(shí),則會(huì)出現(xiàn)了抖動(dòng);

舉個(gè)例子:

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

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

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

當(dāng)時(shí)曾想過(guò)多種技術(shù)辦法來(lái)避免這種問(wèn)題,但是都會(huì)引入更多其他的問(wèn)題,如果你有好的思路,請(qǐng) issue 或者 PR.


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)