項目地址:Netflix Hystrix Github
文章譯自:Hystrix/wiki/How-it-Works
閱讀本文章前,請先了解 Hystrix 的一些基本概念( What is Hystrix )。
流程圖
下圖展示了當使用 Hystrix 構造請求,請求到一個服務依賴項時所發生的流程:
下面部分將更詳細地解釋上述流程:
1. 構造一個 HystrixCommand 或 HystrixObservableCommand 對象
2. 執行 Command
3. 是否緩存 Response
4. 斷路器是否打開
5. 線程池 / 隊列 / 信號量 是否已
6. 執行 HystrixObservableCommand.construct() 或 HystrixCommand.run()
7. 計算線路健康狀況
8. 獲取回調
9. 返回成功的 Response
1. 構造一個 HystrixCommand 或 HystrixObservableCommand 對象
第一步就是構造一個 HystrixCommand 或 HystrixObservableCommand 對象,**Command 對象代表對依賴項的請求。傳遞構造函數在請求發生時所需要的各個參數。
當依賴項預計會返回一個Response時,構造一個 HystrixCommand 對象,如:
HystrixCommand command = new HystrixCommand(arg1, arg2);
當依賴項預計會返回一個可傳播 Response 的 Observable 時(rxJava),構造一個 HystrixObservableCommand 對象,如:
HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);
2. 執行 Command
執行命令有如下四種方式,可以選擇一種方法去執行 Hystrix 的 Command 對象(第一,二個方法只適合簡單的命令對象 HystrixCommand 執行,不適合 HystrixObservableCommand 對象):
execute()
——阻塞,返回從依賴項接收到的響應(或有異常時拋出異常)queue()
——返回一個java.util.concurrent.Future
,可以通過Future.get()
來獲取一個依賴項返回的響應observe()
——訂閱 Observable(這個 Observable 代表從依賴項返回的響應),並返回複製該源 Observable 的 ObservabletoObservable()
——返回一個 Observable,當這個 Observable 被訂閱時,將會執行對應的 Hystrix 的 Command 對象,並且傳播返回的響應
K value = command.execute();
Future<K> fValue = command.queue();
Observable<K> ohValue = command.observe(); //hot observable
Observable<K> ocValue = command.toObservable(); //cold observable
同步調用方法execute()
實際上調用的是 queue().get()
;queue()
反過來調用toObservable().toBlocking().toFuture()
,也就是說所有的 HystrixCommand 的調用最終都是通過 Observable 來實現(netflix 的 oss 項目中大量使用了 rxJava),即使這些命令只是想返回單個值或者一些簡單的值。
3. 是否緩存 Response
如果一個 Hystrix Command 的請求緩存被啓用了,並且這個請求的響應在緩存中可用,那麼這個被緩存的響應將會立即以 Observable 的形式返回(更詳細看下面的“請求緩存”部分)。
4. 斷路器是否開啓
當執行一個命令,Hystrix 會檢查斷路器(circuit-breaker)確認斷路器是否是開啓狀態。
如果斷路器是開啓狀態(或者“跳閘”),那麼 Hystrix 將不會執行這些命令,這些失敗的命令將會被路由到回調邏輯進行後續處理。
如果斷路器是關閉狀態,那麼執行流程將往下走到第5步:檢查是否有足夠的容量來運行該命令
5. 線程池 / 隊列 / 信號量 是否已滿
Hystrix 的執行隔離策略有兩種:Thread 和 Semaphore
如果命令相關聯的線程池和隊列(不是線程模式時,判斷依據爲信號量)已滿,那麼 Hystrix 將不會執行命令,這些失敗的命令也會被路由到回調邏輯進行後續處理。
6. 執行 Command 中的業務邏輯
到這裏,Hystrix 調用對依賴項的這些請求,方式如下:
HystrixCommand.run()
——返回單個響應或拋出異常HystrixObservableCommand.construct()
——返回 Observable 傳播收到的響應或者發送一個onError
的通知
如果run()
或construct()
方法執行時長超過了命令的超時閥值,其線程將拋出一個TimeoutException
(或者在一個單獨的線程拋出,如果命令沒有運行在它自己的線程)。這種情況下 Hystrix 將路由響應到第8步,獲取回調邏輯;並且如果該方法沒有取消或中斷,它將放棄run()
或construct()
方法最終的返回值。
請注意,沒有辦法強制地停止延遲線程——Hystrix 能做的就是在 JVM 上拋出一個 InterruptedException
。如果由 Hystrix 包裝過的邏輯沒有注意處理 InterruptedExceptions
,則 Hystrix 線程池中的線程將會繼續運行,即使客戶端已收到TimeoutException
。這種行爲會填滿 Hystrix 對應命令的線程池,儘管負載是“correctly shed”。很多 Java Http Client 庫沒有說明InterruptedExceptions
,因此請確保在HTTP客戶端上正確配置連接和讀/寫的超時時長。
如果命令沒有拋出異常並且返回了響應,Hystrix 將會在執行一些日誌記錄和度量報告之後返回響應給調用者。如果是通過run()
運行,Hystrix 將返回 Observable 傳播單個響應,然後發送一個onCompleted
的通知;如果是通過construct()
運行,Hystrix 返回與construct()返回的相同的Observable。
7. 計算線路健康狀況
Hystrix 向斷路器報告成功,失敗,拒絕和超時的數據,斷路器維持一組實時跳動的計數器來統計數據。(HystrixCommandMetrics)
斷路器使用這些統計數據來確定電路何時應該“跳閘”,什麼時候該將所有後續的請求短路,直到恢復期過去,並在首次檢查某些健康檢查後會再次關閉電路。
8. 獲取回調
當命令執行失敗時,Hystrix 會試圖恢復到 fallback 狀態:當執行construct()
或run()
時拋出異常(第6步);當命令因爲斷路器跳閘而短路時(第4步);當該命令的線程池隊列或信號量滿時(第5步),或者當命令執行時長超過閥值時。
編寫回調邏輯提供一個通用的響應,以便在不存在任何網絡依賴項或者所有依賴項都失效時,能夠從內存中的緩存中或其他靜態邏輯中獲取一個通用的響應(不至於在單個或多個依賴項失效時,產生連鎖反應帶崩整個應用)。如果必須在回調邏輯中使用網絡請求,應該通過另一個HystrixCommand 或 HystrixObservableCommand 來完成。
在 HystrixCommand 中,在 HystrixCommand.getFallback()
方法中提供自定義的回調邏輯,方法返回單個回調值。
在 HystrixObservableCommand 中,在HystrixObservableCommand.resumeWithFallback()
方法中提供自定義的回調邏輯,方法返回一個 可傳播回調值的 Observable。
如果回調方法返回了響應,Hystrix 會將該響應返回給調用者。在 HystrixCommand 中,Hystrix 返回給調用者一個可傳播響應值的 Observable;在 HystrixObservableCommand 中,Hystrix 將直接返回resumeWithFallback()
返回的 Observable 對象。
如果沒有爲 Hystrix 的命令提供回調邏輯,或者回調邏輯會拋出異常,Hystrix 仍然會返回一個 Observable 對象,但是沒有傳播值並且會立即終止傳播,併發送一個onError
通知。正是這個onError
的通知會將導致命令失敗的異常信息返回給調用者。(用戶提供可能會失敗的回調邏輯是一個很不好的做法,用戶應該提供沒有任何可能執行失敗的回調邏輯)
根據調用 Hystrix 命令方式的不同,失敗時或不存在依賴項時的回調結果也會有所不同:
execute()
——拋出異常queue()
——成功時返回java.util.concurrent.Future
,但如果調用Future.get()
將拋出異常observe()
——返回 Observable 對象,當你訂閱該 Observable 時,將會立即終止並且調用訂閱者的onError
方法toObservable()
——同observe()
9. 返回成功的 Response
如果 Hystrix 命令執行成功,那麼將以 Observable 的形式返回單個響應或多個響應給調用者。取決於上面第2步執行命令的方式不同,返回的 Observable 在返回給你之前會通過以下方式傳播:
- execute()
——和queue()
獲取的方式一樣獲取一個 Future
,然後通過Future.get()
方法通過 Obsevable 獲取返回的單個值
- queue()
——將 Observable 轉換成 BlockingObservable,以便可以轉換成Future
並返回
- observe()
——快速訂閱返回的 Observable 並開始執行訂閱命令的流程;返回 Observable,當被訂閱時可以重新傳播和重新通知
- toObservable()
——返回相同的 Observable;用戶必須訂閱它才能真正開始執行訂閱命令的流程