最近看了一下go-kit久锥,發(fā)現(xiàn)這個(gè)微服務(wù)框架的容斷器瑟由,也是使用sony開源的作為基礎(chǔ)冤寿。
[sony開源在 github 的容斷器](https://github.com/sony/gobreaker)
在源代頭注釋中發(fā)現(xiàn),原來sony實(shí)現(xiàn)的是微軟2015時(shí)公布的CircuitBreaker標(biāo)準(zhǔn)督怜,果然微軟才開源界的大神。
## 1)微軟定義的 Circuit breaker
我不知道正確怎么翻譯,直觀翻譯究流,可能叫:環(huán)形容斷器(或叫:循環(huán)狀態(tài)自動(dòng)切換容斷器)动遭。
因?yàn)樗窃谙旅?個(gè)狀態(tài)循環(huán)切換? :
```
? ? ? ? Closed
? ? ? ? /? ? \
Half-Open <--> Open
初始狀態(tài)是:Closed,指容斷器放行所有請求偷仿。
達(dá)到一定數(shù)量的錯(cuò)誤計(jì)數(shù),進(jìn)入Open 狀態(tài)酝静,指容斷發(fā)生,下游出現(xiàn)錯(cuò)誤宗苍,不能再放行請求薄榛。
經(jīng)過一段Interval時(shí)間后,自動(dòng)進(jìn)入Half-Open狀態(tài)丽啡,然后開始嘗試對成功請求計(jì)數(shù)硬猫。
進(jìn)入Half-Open后,根據(jù)成功/失敗計(jì)數(shù)情況馏予,會自動(dòng)進(jìn)入Closed或Open盔性。
```
## 2)sony開源的go實(shí)現(xiàn)
```go
// 從定義的錯(cuò)誤來看,sony的應(yīng)該增加了對連接數(shù)進(jìn)行了限制 蛹尝。
var (
? ? // ErrTooManyRequests is returned when the CB state is half open and the requests count is over the cb maxRequests
? ? ErrTooManyRequests = errors.New("too many requests")
? ? // ErrOpenState is returned when the CB state is open
? ? ErrOpenState = errors.New("circuit breaker is open")
)
```
### 2.1) 通過Settings的實(shí)現(xiàn)悉尾,了解可配置功能:
```go
type Settings struct {
? ? Name? ? ? ? ? string
? ? MaxRequests? uint32? ? ? ? // 半開狀態(tài)期最大允許放行請求:即進(jìn)入Half-Open狀態(tài)時(shí),一個(gè)時(shí)間周期內(nèi)允許最大同時(shí)請求數(shù)(如果還達(dá)不到切回closed狀態(tài)條件愕难,則不能再放行請求)惫霸。
? ? Interval? ? ? time.Duration // closed狀態(tài)時(shí),重置計(jì)數(shù)的時(shí)間周期猜丹;如果配為0硅卢,切入Open后永不切回Closed--有點(diǎn)暴力藏杖。
? ? Timeout? ? ? time.Duration // 進(jìn)入Open狀態(tài)后脉顿,多長時(shí)間會自動(dòng)切成 Half-open,默認(rèn)60s祥楣,不能配為0汉柒。
? ? // ReadyToTrip回調(diào)函數(shù):進(jìn)入Open狀態(tài)的條件,比如默認(rèn)是連接5次出錯(cuò)兽间,即進(jìn)入Open狀態(tài)正塌,即可對容斷條件進(jìn)行配置。在fail計(jì)數(shù)發(fā)生后乓诽,回調(diào)一次鸠天。
? ? ReadyToTrip? func(counts Counts) bool
? ? // 狀態(tài)切換時(shí)的容斷器
? ? OnStateChange func(name string, from State, to State)
}
```
### 2.2)核心的*執(zhí)行函數(shù)*實(shí)現(xiàn)
```go
func (cb *CircuitBreaker) Execute(req func() (interface{}, error)) (interface{}, error) {
? ? generation, err := cb.beforeRequest() //
? ? if err != nil {
? ? ? ? return nil, err
? ? }
? ? defer func() {
? ? ? ? e := recover()
? ? ? ? if e != nil {
? ? ? ? ? ? cb.afterRequest(generation, false)
? ? ? ? ? ? panic(e) // 如果代碼發(fā)生了panic,繼續(xù)panic給上層調(diào)用者去recover奶段。
? ? ? ? }
? ? }()
? ? result, err := req()
? ? cb.afterRequest(generation, err == nil)
? ? return result, err
}
```
### 2.2 關(guān)鍵? func beforeRequest()
函數(shù)做了幾件事:
0. 函數(shù)的核心功能:判斷是否放行請求剥纷,計(jì)數(shù)或達(dá)到切換新條件剛切換。
1. 判斷是否Closed蹲缠,如是悠垛,放行所有請求。
? ? ? - 并且判斷時(shí)間是否達(dá)到Interval周期,從而清空計(jì)數(shù)因俐,進(jìn)入新周期周偎,調(diào)用toNewGeneration()
2. 如果是Open狀態(tài)撑帖,返回ErrOpenState胡嘿,---不放行所有請求。
? ? ? - 同樣判斷周期時(shí)間衷敌,到達(dá)則 同樣調(diào)用 toNewGeneration(){清空計(jì)數(shù)}
3. 如果是half-open狀態(tài)缴罗,則判斷是否已放行MaxRequests個(gè)請求,如未達(dá)到剛放行面氓;否則返回:ErrTooManyRequests。
4. 此函數(shù)一旦放行請求掘譬,就會對請求計(jì)數(shù)加1(conut.onRequest())呻拌,請求后到另一個(gè)關(guān)鍵函數(shù) : afterRequest()。
### 2.3 關(guān)鍵? func afterRequest()
1. 函數(shù)核心內(nèi)容很簡單酿箭,就對成功/失敗進(jìn)行計(jì)數(shù)趾娃,達(dá)到條件則切換狀態(tài)。
2. 與beforeRequest一樣妇蛀,會調(diào)用公共函數(shù) currentState(now)
? ? ? - currentState(now) 先判斷是否進(jìn)入一個(gè)先的計(jì)數(shù)時(shí)間周期(Interval), 是則重置計(jì)數(shù)笤成,改變?nèi)輸嗥鳡顟B(tài),并返回新一代纵诞。
? ? ? - 如果request耗時(shí)大于Interval, 幾本每次都會進(jìn)入新的計(jì)數(shù)周期培遵,容斷器就沒什么意義了登刺。
## 代碼的核心內(nèi)容
1. 使用了一個(gè)generation的概念嗡呼,每一個(gè)時(shí)間周期(Interval)的計(jì)數(shù)(count)狀態(tài)稱為一個(gè)generation。
2. 在before/after的兩個(gè)函數(shù)中揍很,實(shí)現(xiàn)了兩個(gè)狀態(tài)自動(dòng)切換的機(jī)制:
? ? - 在同一個(gè)generation(即時(shí)間)周期內(nèi)万伤,計(jì)數(shù)滿足狀態(tài)切換條件,即自動(dòng)切換蛉迹;
? ? - 超過一個(gè)generation時(shí)間周期的也會自動(dòng)切換放妈;