熔斷在分布式系統的作用已經被強調過很多次了
可以通過這篇文章來了解價值,Netflix在自己的分布式系統中應用熔斷技術來保護系統
http://blog.51cto.com/developerycj/1950881
內部的實現機制可以參考
https://martinfowler.com/bliki/CircuitBreaker.html
本篇文章將介紹go chassis如何通過熔斷機制,隔離上游服務,保護下游服務。
Go chassis如何保證上游錯誤不影響下游系統
go chassis引用并包裝了https://github.com/afex/hystrix-go帶來了熔斷和降級功能。
當運行時內部處理中的協程達到一定閾值,錯誤率達到一定閾值,或者超時達到一定閾值時,就會觸發熔斷,用戶可按需定制調教熔斷器配置項設定這些參數。
hystrix-go內部的熔斷邏輯
go?chassis使用統一的invocation抽象來代表每一次遠程調用,hystrix-go使用command抽象來封裝任何一個執行片段,invocation會被強制封裝到command中,并在一個circuit中執行。
每個Circuit都是唯一的Name,并且有一個Ticket桶,用來存放ticket,一開始它是關閉狀態,即一切運轉正常
調用將被強制性的包裝進入circuit獨立協程池中,并領取一個ticket。
command最終只有2種狀態,超時,或者完成。每當達到這兩個狀態就會歸還ticket
在這里可以看到ticket機制其實跟限流中的令牌桶算法很像。
當超時或者拿不到ticket時就會被記為一次錯誤,當錯誤達到一定閾值,circuit就會打開,拒絕發送網絡請求
服務級別隔離
每個service內部會有多個circuit,每個circuit對應一個上游微服務。當service3出現問題時(如死鎖,或是并發量太大),將物理進行隔絕,即不再發送任何請求,以保證系統健康,service1依然可以正常和2,4交互,保證大部分業務正常。
這么來看還是很理想的,serivce3的錯誤調用不至于拖垮service1(如果死鎖了,很容易就拖垮service1,導致這個由四個服務組成的系統癱瘓),但真的如此么,讓我們看看層級復雜些的系統。
為何服務級別隔離還不夠?
每個服務都是基于go?chassis開發的
假設api2需要調用service4完成,api1調用3完成,api3調用5完成
service4內的死鎖導致api2失敗了,最終觸發熔斷。service1將整個service2全部隔離了,導致一個小小的死鎖,引發了系統快速失敗。
看上去熔斷在這里反而起到了壞的效果,那讓我們看看沒熔斷會發生什么
不加入熔斷
這時就看哪個客戶端做了超時處理了,因為死鎖的存在,會導致整條調用鏈路掛死,最終導致客戶端端口耗盡后,進而快速失敗
現在來看,死鎖在一個不健壯的系統中是一定會拖垮整個分布式系統的,無解
有熔斷和沒熔斷效果都一樣,最終都是快速失敗。那么如何解決
API級別熔斷
每個circuit只負責一個API的執行,監控,隔離
當service2調用service4時,單獨的接口進入到隔離狀態而不影響其他API調用。
總結
通過這篇文章我們知道了服務級別的錯誤隔離是不夠的,結構不復雜的系統尚可接受,但是復雜后不能因為一個API的錯誤而隔離整個服務,而是細粒度的進行隔離。go?chassis提供了API級別熔斷幫助開發者快速隔離問題服務。
熔斷的手段有超時實踐,并發數,錯誤率等。它強制性的保護起每一次遠程調用,無需開發者自己編寫代碼處理超時,死鎖,網絡錯誤等問題,解放了開發者,讓他們更多的去關注業務代碼而不是分布式系統帶來的復雜性
項目資料
go?chassis開發框架:https://github.com/go-chassis/go-chassis
熔斷文檔:https://go-chassis.readthedocs.io/en/latest/user-guides/cb-and-fallback.html
go?chassis系列文章:?
http://www.lxweimin.com/p/50a0908b81a1