溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

golang熔斷器如何實(shí)現(xiàn)

發(fā)布時間:2022-01-10 10:47:25 來源:億速云 閱讀:151 作者:iii 欄目:開發(fā)技術(shù)

這篇“golang熔斷器如何實(shí)現(xiàn)”文章的知識點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“golang熔斷器如何實(shí)現(xiàn)”文章吧。

熔斷器像是一個保險(xiǎn)絲。當(dāng)我們依賴的服務(wù)出現(xiàn)問題時,可以及時容錯。一方面可以減少依賴服務(wù)對自身訪問的依賴,防止出現(xiàn)雪崩效應(yīng);另一方面降低請求頻率以方便上游盡快恢復(fù)服務(wù)。

熔斷器的應(yīng)用也非常廣泛。除了在我們應(yīng)用中,為了請求服務(wù)時使用熔斷器外,在 web 網(wǎng)關(guān)、微服務(wù)中,也有非常廣泛的應(yīng)用。

1.熔斷器的模式

gobreaker 是基于《微軟云設(shè)計(jì)模式》一書中的熔斷器模式的 Golang 實(shí)現(xiàn)。有 sony 公司開源,目前 star 數(shù)有 1.2K。使用人數(shù)較多。

下面是模式定義的一個狀態(tài)機(jī):

golang熔斷器如何實(shí)現(xiàn)

熔斷器有三種狀態(tài),四種狀態(tài)轉(zhuǎn)移的情況:

  • 熔斷器關(guān)閉狀態(tài),服務(wù)正常訪問

  • 熔斷器開啟狀態(tài),服務(wù)異常

  • 熔斷器半開狀態(tài),部分請求限流訪問

四種狀態(tài)轉(zhuǎn)移:

  • 在熔斷器關(guān)閉狀態(tài)下,當(dāng)失敗后并滿足一定條件后,將直接轉(zhuǎn)移為熔斷器開啟狀態(tài)。

  • 在熔斷器開啟狀態(tài)下,如果過了規(guī)定的時間,將進(jìn)入半開啟狀態(tài),驗(yàn)證目前服務(wù)是否可用。

  • 在熔斷器半開啟狀態(tài)下,如果出現(xiàn)失敗,則再次進(jìn)入關(guān)閉狀態(tài)。

  • 在熔斷器半開啟后,所有請求(有限額)都是成功的,則熔斷器關(guān)閉。所有請求將正常訪問。

2.gobreaker 的實(shí)現(xiàn)

gobreaker 是在上述狀態(tài)機(jī)的基礎(chǔ)上,實(shí)現(xiàn)的一個熔斷器。

2.1熔斷器的定義

type CircuitBreaker struct {  
  name          string  
  maxRequests   uint32  // 最大請求數(shù) (半開啟狀態(tài)會限流)  
  interval      time.Duration   // 統(tǒng)計(jì)周期  
  timeout       time.Duration   // 進(jìn)入熔斷后的超時時間  
  readyToTrip   func(counts Counts) bool // 通過 Counts 判斷是否開啟熔斷。需要自定義  
  onStateChange func(name string, from State, to State) // 狀態(tài)修改時的鉤子函數(shù)  

  mutex      sync.Mutex // 互斥鎖,下面數(shù)據(jù)的更新都需要加鎖  
  state      State  // 記錄了當(dāng)前的狀態(tài)  
  generation uint64 // 標(biāo)記屬于哪個周期  
  counts     Counts // 計(jì)數(shù)器,統(tǒng)計(jì)了 成功、失敗、連續(xù)成功、連續(xù)失敗等,用于決策是否進(jìn)入熔斷  
  expiry     time.Time // 進(jìn)入下個周期的時間  
}

其中,如下參數(shù)是我們可以自定義的:

  • MaxRequests:最大請求數(shù)。當(dāng)在最大請求數(shù)下,均請求正常的情況下,會關(guān)閉熔斷器

  • interval:一個正常的統(tǒng)計(jì)周期。如果為 0,那每次都會將計(jì)數(shù)清零

  • timeout: 進(jìn)入熔斷后,可以再次請求的時間

  • readyToTrip:判斷熔斷生效的鉤子函數(shù)

  • onStateChagne:狀態(tài)變更的鉤子函數(shù)

2.2請求的執(zhí)行

熔斷器的執(zhí)行操作,主要包括三個階段;①請求之前的判定;②服務(wù)的請求執(zhí)行;③請求后的狀態(tài)和計(jì)數(shù)的更新

// 熔斷器的調(diào)用  
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 {  
      // ③ panic 的捕獲  
      cb.afterRequest(generation, false)  
      panic(e)  
    }  
  }()  

  // ② 請求和執(zhí)行  
  result, err := req()  

  // ③ 更新計(jì)數(shù)  
  cb.afterRequest(generation, err == nil)  
  return result, err  
}

2.3請求之前的判定操作

請求之前,會判斷當(dāng)前熔斷器的狀態(tài)。如果熔斷器以開啟,則不會繼續(xù)請求。如果熔斷器半開,并且已達(dá)到最大請求閾值,也不會繼續(xù)請求。

func (cb *CircuitBreaker) beforeRequest() (uint64, error) {  
  cb.mutex.Lock()  
  defer cb.mutex.Unlock()  

  now := time.Now()  
  state, generation := cb.currentState(now)  

  if state == StateOpen { // 熔斷器開啟,直接返回  
    return generation, ErrOpenState  
  } else if state == StateHalfOpen && cb.counts.Requests >= cb.maxRequests { // 如果是半打開的狀態(tài),并且請求次數(shù)過多了,則直接返回  
    return generation, ErrTooManyRequests  
  }  

  cb.counts.onRequest()  
  return generation, nil  
}

其中當(dāng)前狀態(tài)的計(jì)算,是依據(jù)當(dāng)前狀態(tài)來的。如果當(dāng)前狀態(tài)為已開啟,則判斷是否已經(jīng)超時,超時就可以變更狀態(tài)到半開;如果當(dāng)前狀態(tài)為關(guān)閉狀態(tài),則通過周期判斷是否進(jìn)入下一個周期。

func (cb *CircuitBreaker) currentState(now time.Time) (State, uint64) {  
  switch cb.state {  
  case StateClosed:  
    if !cb.expiry.IsZero() && cb.expiry.Before(now) { // 是否需要進(jìn)入下一個計(jì)數(shù)周期  
      cb.toNewGeneration(now)  
    }  
  case StateOpen:  
    if cb.expiry.Before(now) {  
      // 熔斷器由開啟變更為半開  
      cb.setState(StateHalfOpen, now)  
    }  
  }  
  return cb.state, cb.generation  
}

周期長度的設(shè)定,也是以據(jù)當(dāng)前狀態(tài)來的。如果當(dāng)前正常(熔斷器關(guān)閉),則設(shè)置為一個 interval 的周期;如果當(dāng)前熔斷器是開啟狀態(tài),則設(shè)置為超時時間(超時后,才能變更為半開狀態(tài))。

2.4請求之后的處理操作

每次請求之后,會通過請求結(jié)果是否成功,對熔斷器做計(jì)數(shù)。

func (cb *CircuitBreaker) afterRequest(before uint64, success bool) {  
  cb.mutex.Lock()  
  defer cb.mutex.Unlock()  

  now := time.Now()  

  // 如果不在一個周期,就不再計(jì)數(shù)  
  state, generation := cb.currentState(now)  
  if generation != before {  
    return  
  }  

  if success {  
    cb.onSuccess(state, now)  
  } else {  
    cb.onFailure(state, now)  
  }  
}

如果在半開的狀態(tài)下:

如果請求成功,則會判斷當(dāng)前連續(xù)成功的請求數(shù) 大于等于 maxRequests, 則可以把狀態(tài)由半開狀態(tài)轉(zhuǎn)移為關(guān)閉狀態(tài)
如果在半開狀態(tài)下,請求失敗,則會直接將半開狀態(tài)轉(zhuǎn)移為開啟狀態(tài)
如果在關(guān)閉狀態(tài)下:

如果請求成功,則計(jì)數(shù)更新
如果請求失敗,則調(diào)用 readyToTrip 判斷是否需要將狀態(tài)關(guān)閉狀態(tài)轉(zhuǎn)移為開啟狀態(tài)

以上就是關(guān)于“golang熔斷器如何實(shí)現(xiàn)”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI