天天看點

用golang實作一個熔斷器

首先貼出倉庫位址:https://github.com/JeffreyDing11223/goBreaker

這個熔斷器實作簡單,使用友善,同時大家也可以fork後進行自己需要的改造,當然,也歡迎大家提pr,一起優化goBreaker

下面介紹下goBreaker:

goBreaker狀态機

用golang實作一個熔斷器

狀态轉換邏輯

  • 初始為closed狀态,一旦遇到請求失敗時,會觸發熔斷檢測(見下方的 ShouldTrip),熔斷檢測來決定是否将狀态從closed轉為open。
  • 當熔斷器為open狀态時,會熔斷所有目前服務要發出去的請求,直到冷卻時間(見下方的CoolingTimeout)結束,會從open轉變為half-open狀态。
  • 當熔斷器為half-open狀态時,以檢測時間(見下方的 DetectTimeout)為周期去發送請求。請求成功則計數器加1,當計數器達到一定門檻值時則轉為closed狀态;請求失敗則轉為open狀态。

熔斷器内部資料結構

type Breaker struct {
	Container // contains all success, error and timeout
	sync.RWMutex

	state           State
	openTime        time.Time // the time when the breaker become OPEN
	lastRetryTime   time.Time // last retry time when in HALFOPEN state
	halfopenSuccess int       // consecutive successes when HALFOPEN

	options Options

	now func() time.Time
}
           

Breaker是暴露在最外層的struct,由以下屬性組成:

  • Container:是一個interface,被 window 實作,負責熔斷器請求失敗,成功的相關計算和統計
  • RW鎖:在http-gateway中,針對每個cmd有一個熔斷器,每個 cmd 同時會有多個goroutine并發請求,需要RW鎖來保持熔斷器中計數器,狀态等等的同步
  • state:熔斷器三種狀态,closed,open 和 half-open
  • openTime:當熔斷器變為 open 狀态時,記錄下的時間
  • lastRetryTime:在 half-open 狀态時,會有個檢測周期,即每隔這個周期之後,熔斷器會放請求出去,同時更新這個 lastRetryTime。
  • halfopenSuccess:在 half-open狀态時,當請求成功時,halfopenSuccess 會+1,當 halfopenSuccess 等于一個門檻值時(預設為2),則變為 closed 狀态
  • options:Breaker 的配置項,包括桶持有數量持有時間,冷卻時間,檢測周期,熔斷檢測回調和狀态變化回調等等
// Options for Breaker
type Options struct {
	// parameters for container
	BucketTime time.Duration // the time each bucket holds
	BucketNums int           // the number of buckets the breaker have

	// parameters for breaker
	BreakerRate        float64
	BreakerMinQPS      int           // when instance > 1, if qps is over this value, the breaker trip will work
	BreakerMinSamples  int           // for RateTrip callback
	CoolingTimeout     time.Duration // fixed when create
	DetectTimeout      time.Duration // fixed when create
	HalfOpenSuccess    int
	ShouldTrip         TripFunc // trip callback, default is RateTrip func
	StateChangeHandler StateChangeHandler

	now func() time.Time
}
           

options是Breaker的配置項,有以下屬性組成:

  • BucketTime:桶的線上時間
  • BucketNums:window下持有桶的數量
  • BrekaerRate:熔斷檢測回調RateTrip的門檻值
  • BreakerMinQPS:當執行個體數量大于1時,并且開啟了動态政策時,用于計算BreakerMinSamples
  • BreakerMinSamples:最小采樣數,配合RateTrip熔斷檢測回調使用
  • CoolingTimeout:保持 open 狀态直到冷卻時間結束,會從 open 轉變為 half-open 狀态,預設為5秒
  • DetectTimeout:half-open 狀态時,每隔這個周期之後,熔斷器會放請求出去
  • HalfOpenSuccess:half-open狀态變為closed狀态的判斷名額
  • ShouldTrip:熔斷檢測回調,為nil則代表不啟用熔斷功能
  • StateChangeHandler:狀态變化回調
type window struct {
	sync.Mutex
	oldest  int       // oldest bucket index
	latest  int       // latest bucket index
	buckets []*bucket // buckets this window has

	bucketTime time.Duration // time each bucket holds
	bucketNums int           // the largest number of buckets of window could have
	expireTime time.Duration // expire time of this window, equals to window size
	inWindow   int           // the number of buckets in the window currently

	conseErr int64 //consecutive errors
}
           

window負責熔斷器請求失敗,成功的相關計算和統計,有以下屬性組成:

  • 互斥鎖:保證内部資料同步
  • oldest:最老的桶,由 latest 桶變化而來,用于視窗下所有請求結果的存儲
  • latest:最新的桶,每次統計請求結果時,用最新的桶來存儲
  • buckets:所有桶
  • bucketTime:latest 桶的線上時間,一旦 latest 桶下線,則變為 oldest 桶
  • bucketNums:視窗最大持有桶的數量
  • expireTime:oldest 桶的過期時間,一旦 oldest 桶過期,則從 window 中“移去”,expireTime = bucketTime*bucketNums
  • inWindow:視窗當下持有桶的數量
  • conseErr:連續錯誤數量,每次請求結果為成功時便清零

熔斷檢測回調:

  1. ThresholdTripFunc:當失敗和逾時的總數超過門檻值,則熔斷
  2. ConsecutiveTripFunc:當連續錯誤總數(conseErr)超過門檻值,則熔斷
  3. RateTripFunc:當視窗内請求總數大于最小采樣數且錯誤率(失敗+逾時數量/請求總數)大于一定值時,則熔斷

api

  • InitCircuitBreakers方法作為初始化熔斷器使用,這裡用cmd來區分各個breaker
  • BreakerWhitelist 可以配置熔斷白名單,在白名單中的cmd不參與熔斷
  • IsTriggerBreaker 判斷目前cmd的熔斷器的狀态,并告訴上層
  • api比較簡單,可以直接閱讀源碼來決定如何使用,倉庫位址見文首