您好,登錄后才能下訂單哦!
在分布式環境中,各個微服務相互調用,當某些情況下,比如后端中間件服務故障、第三方服務中斷導致某個服務無限期不可用,短時間無法恢復,則可能會導致連鎖故障,最終影響壓垮整個業務集群
斷路器模式不同于重試模式,重試模式是使應用程序可以重試操作以期望它會成功,而斷路器模式是防止應用程序執行一個可能失敗的操作,減少執行可能失敗操作的CPU、內存、線程等資源的浪費,從而保證服務的整體可用
<!--more-->
斷路器相當于一個請求操作執行的代理,托管請求操作的執行
實現原理流程:
斷路器狀態機實現上有三種狀態:Closed(斷路器關閉)、Open(開放)、HalfOpen(半開放)
狀態 | 說明 | 備注 |
---|---|---|
Closed | 關閉 | 斷路器關閉正常執行操作 |
Open | 打開 | 斷路器開放,所有請求直接返回錯誤,不執行任何請求 |
HalfOpen | 半開放 | 允許有限數量的請求通過,如果執行成功,恢復到關閉狀態,如果仍然失敗,則恢復到開放,然后重新啟動超時定時器 |
#斷路器實現
斷路器實現實現主要分為三部分:狀態統計、狀態轉移、請求執行
狀態統計:統計已經執行的請求的成功失敗的數量,以確定是否需要進行狀態轉移
狀態轉移:根據當前統計信息和當前狀態來進行目標狀態的確定及轉移操作
請求執行:代理前端任務的執行,如果當前狀態不需要進行嘗試執行,就直接返回錯誤,避免資源浪費
Golang里面已經有開源的實現,https://github.com/sony/gobreaker/blob/, 接下來救市剖析它的實現
Counts就是一個計數器,記錄當前請求成功和失敗的數量
type Counts struct {
Requests uint32 // 請求數
TotalSuccesses uint32 // 成功
TotalFailures uint32 // 失敗
ConsecutiveSuccesses uint32 // 連續成功
ConsecutiveFailures uint32 // 連續失敗
}
計數器完成對應請求狀態的次數,為后續狀態轉移提供數據, Counts提供了onRequest、onSuccess、onFailure、clear幾個輔助接口用于實現對應請求狀態的操作,感興趣可以看下
type CircuitBreaker struct {
name string
// maxRequests限制half-open狀態下最大的請求數,避免海量請求將在恢復過程中的服務再次失敗
maxRequests uint32
// interval用于在closed狀態下,斷路器多久清除一次Counts信息,如果設置為0則在closed狀態下不會清除Counts
interval time.Duration
// timeout進入open狀態下,多長時間切換到half-open狀態,默認60s
timeout time.Duration
// readyToTrip熔斷條件,當執行失敗后,會根據readyToTrip決定是否進入Open狀態
readyToTrip func(counts Counts) bool
// onStateChange斷路器狀態變更回調函數
onStateChange func(name string, from State, to State)
mutex sync.Mutex
//. state 斷路器狀態
state State
// generation 是一個遞增值,相當于當前斷路器狀態切換的次數, 為了避免狀態切換后,未完成請求對新狀態的統計的影響,如果發現一個請求的generation同當前的generation不同,則不會進行統計計數
generation uint64
// Counts 統計
counts Counts
// expiry 超時過期用于open狀態到half-open狀態的切換,當超時后,會從open狀態切換到half-open狀態
expiry time.Time
}
請求執行,對外開放的請求執行接口
func (cb *CircuitBreaker) Execute(req func() (interface{}, error)) (interface{}, error) {
// 執行請求鉤子,會根據當前狀態,來返回當前的generation和err(如果位于open和half-open則不為nil), 通過err來進行判斷是否直接返回
generation, err := cb.beforeRequest()
if err != nil {
return nil, err
}
// 捕獲panic,避免應用函數錯誤造成斷路器panic
defer func() {
e := recover()
if e != nil {
cb.afterRequest(generation, false)
panic(e)
}
}()
// 執行請求
result, err := req()
// 根據結果來進行對應狀態的統計, 同時傳遞generation
cb.afterRequest(generation, err == nil)
return result, err
}
func (cb *CircuitBreaker) beforeRequest() (uint64, error) {
cb.mutex.Lock()
defer cb.mutex.Unlock()
// 獲取當前的狀態
now := time.Now()
state, generation := cb.currentState(now)
// open和half-open狀態則直接返回
if state == StateOpen {
return generation, ErrOpenState
} else if state == StateHalfOpen && cb.counts.Requests >= cb.maxRequests {
// 避免海量請求對處于恢復服務的影響,這里有一個限流的操作,避免請求數超過最大請求數
return generation, ErrTooManyRequests
}
// 統計狀態
cb.counts.onRequest()
return generation, nil
}
func (cb *CircuitBreaker) afterRequest(before uint64, success bool) {
cb.mutex.Lock()
defer cb.mutex.Unlock()
// 重新獲取狀態
now := time.Now()
state, generation := cb.currentState(now)
// 如果前后狀態不一致,則不計數
if generation != before {
return
}
// 根據狀態計數
if success {
cb.onSuccess(state, now)
} else {
cb.onFailure(state, now)
}
}
func (cb *CircuitBreaker) currentState(now time.Time) (State, uint64) {
switch cb.state {
case StateClosed:
// 如果當前當前是closed狀態,并且有設置expiry,則遞增Generation到新一輪統計計數
if !cb.expiry.IsZero() && cb.expiry.Before(now) {
cb.toNewGeneration(now)
}
case StateOpen:
// 如果是Open狀態,并且超時,則嘗試到半打開狀態
if cb.expiry.Before(now) {
cb.setState(StateHalfOpen, now)
}
}
return cb.state, cb.generation
}
func (cb *CircuitBreaker) toNewGeneration(now time.Time) {
// 遞增generation, 清除狀態
cb.generation++
cb.counts.clear()
// 設置超時時間
var zero time.Time
switch cb.state {
case StateClosed:
if cb.interval == 0 {
cb.expiry = zero
} else {
cb.expiry = now.Add(cb.interval)
}
case StateOpen:
cb.expiry = now.Add(cb.timeout)
default: // StateHalfOpen
cb.expiry = zero
}
}
斷路器比較適合針對遠程服務或者第三方服務的調用,如果該操作極有可能會失敗,則斷路器可以盡可能的減小失敗對應用的影響,避免資源浪費
但缺點也顯而易見,斷路器本身相當于一層代理,在應用程序執行進行統計和控制,本身就有一定的資源消耗,同時內部基于synx.Mutex鎖來實現,高并發下肯定會有鎖爭用問題,可能需要根據業務來使用多個斷路器,來分散這種鎖爭用,同時應該避免在斷路器req函數內,去執行重試和過長時間的超時等待,因為斷路器核心是快速失敗
更多文章可以訪問http://www.sreguide.com/
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。