一般在微服架構中,有一個組件角色叫熔斷器。顧名思義,熔斷器起的作用就是在特定的場景下關掉當前的通路,從而起到保護整個系統的效果。
在微服務架構中,一般我們的獨立服務是比較多的,每個獨立服務之間划分責任邊界,並通過約定協議接口來進行通信。當我們的調用鏈路復雜依賴多時,很可能會發生雪崩效應。
假設有這么一個場景,有A, B, C, D四個獨立服務,A會依賴B,C,D;當D發生負載過高或網絡異常等導致響應過慢或超時時,很可能A會因此堆積過多的等待鏈接,從而導致A的狀態也轉為異常,后面依賴到A的其他服務跟着發生鏈式反應,這將會導致大面積的服務不可用,即使本來是一些沒有依賴到B,C,D的服務。如下圖所示: 
這不是我們希望看到的結果,所以這個時候熔斷器可以派上用場。最簡單的做法,我們為每個依賴服務配置一個熔斷器開關,正常情況下是關閉的,也就是可以正常發起請求;當請求失敗(超時或者其他異常)次數超過預設值時,熔斷器自動打開,這時所有經過這個熔斷器的請求都會直接返回失敗,並沒有真正到達所依賴的服務上。這時服務A本身仍然是能正常服務的, 如下圖所示: 
那么熔斷器具體又是怎么工作的呢?來看下,一個擁有基本功能的熔斷器的狀態機大體是這樣子的: 
主要在三種狀態中轉換:
- 關閉狀態
當熔斷器處於關閉狀態時,請求是可以被放行的;
當熔斷器統計的失敗次數觸發開關時,轉為打開狀態。 - 打開狀態
當熔斷器處於打開狀態時,所有請求都是不被放行的,直接返回失敗;
只有在經過一個設定的時間窗口周期后,熔斷器才會轉換到半開狀態 - 半開狀態
當熔斷器處於半開狀態時,當前只能有一個請求被放行;
這個被放行的請求獲得遠端服務的響應后,假如是成功的,熔斷器轉換為關閉狀態,否則轉換到打開狀態。
最后,基於這個狀態機,我用Golang實現了一個只包含最基本功能的熔斷器:github.com/moxiaomomo/circuitbreaker, 有興趣可以參考一下,也歡迎指正。
主要用法如下:
// 創建一個熔斷器實例,指定熔斷時間窗口和失敗觸發開關閾值等 cbs := NewCirucuitBreaker(time.Second, 150, 20) // 向熔斷器注冊command(可以理解為對應的服務請求id) testcmd := "call_serviceB" suc := cbs.RegisterCommandAsDefault(testcmd) // 向熔斷器報告當前command的執行結果(成功或失敗) cbs.Report(testcmd, false) cbs.Report(testcmd, true) // ... // 向熔斷器詢問當前該command是否能被執行 execAllow := cbs.AllowExec(testcmd)
