思考的過程每每比直接獲得結論更加劇要html
在分佈式環境下,不可避免的就是服務之間的調用。A 調 B,B 可能會失敗,若是此時 B 服務掛掉,那麼會致使服務 A 由於服務 B 的失敗而失敗。從而致使 客戶端認爲 A 也是失敗的。 簡單說就是,牽一髮而動全身java
這也是,咱們須要熔斷器的緣由。咱們須要有保護服務調用的組件。當服務 B 掛掉時,服務 B 須要可以快速失敗。git
思考一下,什麼是隔離?
我對他的理解,大概就是,服務 A 調用服務 B,服務 C。不能由於調用服務 B 出現問題,而致使調用服務 C 也出現問題。也就是,服務B、服務C 的調用應該放在不一樣的環境下。github
常見的資源隔離,有線程池隔離,信號量隔離算法
線程池隔離 | 信號量隔離 | |
---|---|---|
線程 | 請求線程和調用 provider 不是同一個線程 | 請求線程和調用 provider 是同一個線程 |
開銷 | 排隊、調度、上下文開銷等 | 無線程切換,開銷低 |
異步 | 支持 | 不支持 |
併發支持 | 支持(線程池大小) | 支持(信號量上限) |
傳遞 Header | 沒法傳遞 Http Header | 能夠傳遞 Http Header |
當 provider 提供的服務不可用,或出現異常時,應該有可供回調的降級方法。編程
當 provider 被大量調用時,爲了保護鏈路,須要作限流。那麼其實核心的問題是,如何進行統計。設計模式
每一個 provider 的調用狀況須要統計。這樣能夠更好的監控到 provider 提供的服務的狀況。統計的算法,應該是基於滑動窗口進行統計。tomcat
由於有對每一個 provider 調用狀況統計,在調用以前,失敗次數達到某個閾值時,能夠認爲該 provider 已是有問題的,能夠直接快速失敗,以防止服務雪崩狀況。併發
熔斷機制是針對每一個 provider 的。可是,有可能服務系統已經要達到極限了,不能再接收任何請求了,那麼此時,出於系統的保護,也應該快速失敗。異步
做爲一個組件,支持擴展那是必須。
常見的擴展方式:(其實就是面向接口編程,在某些執行流程中,暴露出部分接口。剩下的就看你怎麼封裝了)
public interface SayListener { String say(); } public class App { List<SayListener> sayListener = new ArrayList<>(); public void doSomething() { // ... sayAction(); // 觸發了 say 的動做 if (null != sayListener && sayListener.size() > 0) { sayListener.forEach(listener -> { listener.say(); }); } // ... } public void sayAction() {} public void registerSayListener(SayListener listener) { sayListener.add(listener); } }
sentinel | hystrix | resilience4j | |
---|---|---|---|
隔離策略 | 信號量隔離(併發線程數限流) | 線程池隔離/信號量隔離 | 信號量隔離 |
熔斷降級策略 | 基於響應時間、異常比率、異常數 | 基於異常比率 | 基於異常比率、響應時間 |
實時統計實現 | 滑動窗口(LeapArray) | 滑動窗口(RxJava) | Ring Bit Buffer |
動態規則配置 | 支持多種數據源 | 支持多種數據源 | 有限支持 |
擴展性 | 多個擴展點 | 插件形式 | 接口形式 |
基於註解的支持 | 支持 | 支持 | 支持 |
限流 | 基於 QPS, 支持基於調用關係的限流 | 基於線程池個數有限支持 | Rate Limiter |
系統自適應保護 | 支持 | 不支持 | 不支持 |
控制檯 | 豐富 | 簡單 | 不提供控制檯,可對接其餘監控系統 |
hystrix wiki 介紹實在太全了,這裏就不必在介紹了,主要是從我的的角度去思考若是讓本身也實現一個斷路器中間件,你會怎麼作?(鑑於 hystrix 中止維護,就沒看源碼了,以及 sentinel 的活躍,重心會放在 sentinel 上)
在使用 hystrix 須要注意的幾個地方。
設置 hystrix 超時時間時,須要大於等於 ribbon 的超時時間。 例如 ribbon 的配置以下
ReadTimeout=1000 ConnectTimeout=1000 MaxAutoRetries=1 MaxAutoRetriesNextServer=1
那麼 hystrix 的超時時間公式爲:
execution.isolation.thread.timeoutInMilliseconds >= (MaxAutoRetriesNextServer + 1) * (MaxAutoRetries + 1) * (ReadTimeout + ConnectTimeout)
由於使用線程池隔離時(execution.isolation.strategy=THREAD),會新建立一個線程,所以 tomcat 線程的變量則沒法傳遞給新的線程。那麼此時可使用 信號量隔離(execution.isolation.strategy=SEMAPHORE)。由於使用信號量隔離時,會使用 tomcat 線程調用遠程服務。
其實 hystrix 也想到了會有這樣的需求, 所以在 wiki 中,推薦咱們繼承 HystrixConcurrencyStrategy,重寫 wrapCallable() 方法。 具體代碼參考 線程池隔離傳遞 ThreadLocal 值
該做者是基於 spi 實現的(牛逼),其實 hystrix 提供了可配置的參數供咱們配置
hystrix: plugin: HystrixConcurrencyStrategy: implementation: com.csp.hystrix.MyHystrixConcurrencyStrategy