Hystrix是一個用於處理分布式系統的延遲和容錯的開源庫,在分布式系統里,許多依賴不可避免的會調用失敗,比如超時,異常等,Hystrix能保證在一個依賴出問題的情況下,不會導致整體服務失敗,避免級聯故障,以提高分布式系統的彈性。
“斷路器” 本身是一種開關設置,當某個服務單元發生故障之后,通過斷路器的故障監控(類似熔斷保險絲),向調用方返回一個符合預期的,可處理的備選相應(fallBack),而不是長時間的等待或者拋出調用方法無法處理的異常,這樣就保證了服務調用方的線程不會長時間,不必要的占用,從而避免了故障在分布式系統中的蔓延,乃至雪崩。
在大中型分布式系統中,通常系統很多依賴(HTTP,hession,Netty,Dubbo等),如下圖:

在高並發訪問下,這些依賴的穩定性與否對系統的影響非常大,但是依賴有很多不可控問題:如網絡連接緩慢,資源繁忙,暫時不可用,服務脫機等.
如下圖:QPS為50的依賴 I 出現不可用,但是其他依賴仍然可用.

當依賴I 阻塞時,大多數服務器的線程池就出現阻塞(BLOCK),影響整個線上服務的穩定性.如下圖:

在復雜的分布式架構的應用程序有很多的依賴,都會不可避免地在某些時候失敗。高並發的依賴失敗時如果沒有隔離措施,當前應用服務就有被拖垮的風險。
解決問題方案:對依賴做隔離,Hystrix就是處理依賴隔離的框架,同時也是可以幫我們做依賴服務的治理和監控.
服務熔斷:
熔斷機制是應對雪崩效應的一種微服務鏈路保護機制。
當扇出鏈路(即上面的圖二)的某個微服務(I)不可用或者響應時間太長,會進行服務的降級,進而熔斷該節點微服務的調用,快速返回"錯誤"的響應信息。當檢測到該節點微服務調用響應正常回復后恢復調用鏈路。在springCloud框架里熔斷機制通過Hystrix實現。Hystrix會監控微服務服務間調用的狀況,當失敗的調用到達一定的閾值,缺省是5秒內20次調用失敗就會啟動熔斷機制,熔斷機制的注解是 @HystrixCommand
基於springcloud 學習中 Ribbon的demo進行改造 :https://www.cnblogs.com/wuzhenzhao/category/1529973.html
1.pom文件修改 添加以下依賴
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
2.application.yml 不需要修改修改
3. 修改Controller 接口 ,添加注解 @HystrixCommand
@RestController public class RibbonController { // private static final String REST_URL_PREFIX="http://localhost:8001"; 單機版 //集群的時候 需要配置該服務在eureka里注冊的名字
private static final String REST_URL_PREFIX="http://cloud-provider"; @Autowired private RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "processHystrix_Get")//熔斷機制
@RequestMapping(value ="/hello") public String get(Long id) { Map map =new HashMap<>(); map.put("id",id); return restTemplate.getForObject(REST_URL_PREFIX+"/hello?id={id}", String.class,map); } //消費端可以調用服務發現
@RequestMapping(value ="/discovery") public Object discovery() { return restTemplate.getForObject(REST_URL_PREFIX+"/discovery", Object.class); } public String processHystrix_Get(Long id) { return "hello Hystrix"; } }
修改原來的cloud-provider服務:
@GetMapping("/hello") public String helloEureka(String id){ if (StringUtils.isEmpty(id)) { throw new RuntimeException(); } return "Hello Eureka Provider"; }
4.修改主啟動類
@EnableDiscoveryClient @SpringBootApplication //自定義負載均衡算法 自定義配置類不能跟主啟動類一個包或在子包下面 //name: 表示對哪個服務采用自定義算法 //configuration:負載算法類
@RibbonClient(name="cloud-provider") @EnableCircuitBreaker // 對Hystrix熔斷機制的支持
public class RibbonApp { private final static Logger log = LoggerFactory.getLogger(RibbonApp.class); public static void main(String[] args) { SpringApplication.run(RibbonApp.class,args); log.info("服務啟動成功"); } }
這樣就配置好了服務熔斷,當某個接口發生異常時,就會跳轉進配置的方法。
原理分析(摘自《Spring Cloud 微服務實戰》):
通過上面的快速入門示例,我們對Hystrix的使用場景 和使用方法已經有了一 個基礎的認識。 接下來我們通過解讀NetflixHystrix官方的流程圖來詳細了解一 下:當 一 個請求調用了相關服務依賴之后Hystrix是如何工作的(即如上例中所示,當訪問了http://localhost:9001/hello請求之后, 在RIBBON-SERVER中是如何處理的)。
工作流程:
下面我們根據圖中標記的數字順序來解釋每 一 個環節的 詳細內容。

1. 創建HystrixCommand或HystrixObservableCommand對象:
首先,構建 一 個HystrixCommand或是HystrixObservableCommand對象,用來表示對依賴服務的操作請求, 同時傳遞所有需要的參數。 從其命名中我們就能知道它采用了“ 命令模式 ”來實現對服務調用操作的封裝。關於命令模式可以參考:https://www.cnblogs.com/wuzhenzhao/p/12557341.html
而這兩個 Command 對象分別針對不同的應用場景。
- HystrixCommand: 用在依賴的服務返回單個操作結果的時候。
- HystrixObservableCommand: 用在依賴的服務返回多個操作結果的時候。
命令模式主要包含四種角色:
- 接收者角色(Receiver) :該類負責具體實施或執行一個請求;
- 命令角色(Command) :定義需要執行的所有命令行為;
- 具體命令角色(Concrete Command) 該類內部維護一個接收者(Receiver) 在其execute()方法中調用Receiver的相關方法;
- 請求者角色(Invoker) :接收客戶端的命令, 並執行命令。
我們可以看到,調用者Invoker與操作者Receiver通過 Command命令接口實現了解耦。對於調用者來說, 我們可以為其注入多個命令操作, 比如新建文件、復制文件、 刪除文件這樣三個操作, 調用者只需在需要的時候直接調用即可, 而不需要知道 這些操作命令實際是如何實現的。 而在這里所 提到的 HystrixComrnand 和 HystrixObservableComrnand 則是在 Hystrix中對 Command 的進 一 步抽象定義。
從上面的命令模式示例中我們也可以發現, Invoker和 Receiver 的關系非常類似於 “ 請求-響應 ” 模式, 所以它比較適用於實現記錄日志、 撤銷操作、 隊列請求等。在下面這些情況下應考慮使用命令模式。
- 使用命令模式作為“回調 (CallBack) "在面向對象系統中的替代。"CallBack" 講的便是先將一個函數登記上, 然后在以后調用此函數。
- 需要在不同的時間指定請求、 將請求排隊。一 個命令對象和原先的請求發出者可以有不同的生命期。 換言之, 原先的請求發出者可能已經不在了, 而命令對象本身仍然是活動的。這時命令的接收者可以是在本地, 也可以在網絡的另外 一 個地址。命令對象可以在序列化之后傳送到另外 一 台機器上去。
- 系統需要支持命令的撤銷。命令對象可以把狀態存儲起來, 等到客戶端需要撤銷命令所產生的效果時, 可以調用 undo() 方法, 把命令所產生的效果撤銷掉。命令對象還可以提供 redo()方法, 以供客戶端在需要時再重新實施命令效果。
- 如果要將系統中所有的數據更新到日志里,以便在系統 崩潰時,可以根據日志讀回所有的數據更新命令, 重新 調用 Execute()方法一 條 一 條執行 這些命令, 從而恢復系統在崩潰前所做的數據更新。
2.命令執行:
從圖中我們可以看到一 共存在4種命令的執行方式,而 Hystrix在執行 時 會根據創建的Command對象以及具體的情況來選擇 一 個執行。其中HystrixComrnand實現了下面兩個執行方式。
- execute (): 同步執行,從依賴的服務 返回一 個單 一的結果對象, 或是在發生錯誤的時候拋出異常。
- queue (): 異步執行,直接返回一 個Future對象, 其中包含了服務 執行 結束時要返回的單一結果對象。
而HystrixObservableCommand實現了另外兩種 執行方式。
- observe () : 返回Observable對象,它代表了操作的多個結果,它是一 個Hot Observable。
- toObservable(): 同樣會返回Observable對象, 也代表了操作的多個結果,但它返回的是一 個Cold Observable。
在Hystrix的底層實現中大量地使用了RxJava,在這里對RxJava的觀察者-訂閱者模式做 一 個簡單的入門介紹。上面我們所提到的Observable對象就是RxJava中的核心內容之 一 ,可以把它理解為“事件源”或是“被觀察者 ” , 與其對應的Subscriber對象,可以理解為“訂閱者 ”或是“觀察者 ” 。 這兩個對象 是RxJava響應式編程的重要組成部分。
- Observable用來向訂閱者Subscriber對象 發布事件,Subscriber對象則在接收到事件后對其進行處理, 而在這里所指的事件 通常就是對依賴 服務的調用。
- 一 個Observable可以發出多個事件, 直到結束或是 發生異常。
- Observable 對象每發出一 個事件,就會調用對應 觀察者 Subscriber 對象的onNext ()方法。
- 每一 個Observable的執行,最后 一 定會通過調用 Subscriber. onCompleted () 或者Subscriber.onError()來結束該事件的操作流。
下面我們通過 一 個簡單的例子來直觀理解 一 下 Observable與Subscribers:
public class RxJavaTest { public static void main(String[] args) { Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello RxJava"); subscriber.onNext("I am程序猿"); subscriber.onCompleted(); } }); //創建訂閱者subscriber
Subscriber<String> subscriber = new Subscriber<String>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(String s) { System.out.println("Subscriber : " + s); } }; //訂閱
observable.subscribe(subscriber); } }
在該示例中, 創建了 一 個簡單的事件源 observable,一 個對事件傳遞內容輸出的訂閱者 subscriber, 通過 observable.subscribe(subscriber) 來觸發事件的發布。在這里我們對於事件源 observable 提到了兩個不同的概念: Hot Observable 和 Cold Observable, 分別對應了上面 command. observe ()和command.toObservable() 的返回對象。 其中 Hot Observable, 它不論” 事件源 ”是否有“ 訂閱者 ” , 都會在創建后對事件進行發布, 所以對於 Hot Observable 的每 一 個“ 訂閱者 ”都有可能是從“事件源”的中途開始的, 並可能只是看到了整個操作的局部過程。 而 Cold Observable 在沒有“ 訂閱者 ”的時候並不會發布事件, 而是進行等待, 直到有“ 訂閱者 ”之后才發布事件, 所以對於 ColdObservable 的訂閱者, 它可以保證從 一 開始看到整個操作的全部過程。大家從表面上可能會認為只是在 HystixObservableCommand 中使用了 RxJava,然而實際上execute()、 queue()也都使用了Rx.Java來實現。 從下面的源碼中我們可以看到:
- execute ()是通過queue()返回的異步對象Future<R>的get()方法來實現同步執行的。 該方法會等待任務執行結束, 然后獲得R類型的結果進行返回。
- queue ()則是通過toObservable()來獲得 一 個Cold Observable, 並且通過 toBlocking ()將該Observable轉換成BlockingObservable, 它可以把數據以阻塞的方式 發射出來。 而toFuture 方法則是 把BlockingObservable轉換為一 個Future, 該方法只是創建 一 個Future 返回並不會阻塞,這使得消費者可以自己決定如何處理異步操作。 而execute()就是直接使用了queue()返回的 Future中的阻塞方法 get()來實現同步操作的。 同時通過這種方式轉換的Future要求Observable 只發射 一 個數據,所以 這兩個實現都只能返回單 一 結果。
public R execute() { try { return queue().get(); } catch (Exception e) { throw Exceptions.sneakyThrow(decomposeException(e)); } } public Future<R> queue() { /* * The Future returned by Observable.toBlocking().toFuture() does not implement the * interruption of the execution thread when the "mayInterrupt" flag of Future.cancel(boolean) is set to true; * thus, to comply with the contract of Future, we must wrap around it. */ final Future<R> delegate = toObservable().toBlocking().toFuture(); final Future<R> f = new Future<R>() {
if (f.isDone()) { ...... } return f; }
3.結果是否被緩存:
若當前命令的請求緩存功能是被啟用的, 並且該命令緩存命中, 那么緩存的結果會立即以Observable 對象的形式 返回。
4.斷路器是否打開:
在命令結果沒有緩存命中的時候, Hystrix在執行命令前需要檢查斷路器是否為打開狀態:
- 如果斷路器是打開的,那么Hystrix不會執行命令,而是轉接到fallback處理邏輯(第8步)
- 如果斷路器是關閉的, 那么Hystrix跳到第5步,檢查是否有可用資源來 執行命令。
5.線程池請求隊列信號量是否占滿:
如果與命令相關的線程池和請求隊列,或者信號量(不使用線程池的時候)已經被占滿, 那么Hystrix也不會執行命令,而是轉接到fallback處理邏輯(第8步)。需要注意的是,這里Hystrix所判斷的線程池並非容器的線程池,而是每個依賴服務的專有線程池。 Hystrix為了保證不會因為某個依賴服務的間題影響到其他依賴服務而采用了“ 艙壁模式" (Bulkhead Pattern)來 隔離每個依賴的服務。下面介紹一下依賴隔離
“艙壁模式”對於熟悉 Docker 的讀者 一 定不陌生, Docker 通過“艙壁模式”實現進程的隔離, 使得容器與容器之間不會互相影響。 而 Hystrix 則使用該模式實現線程池的隔離,它會為每 一 個依賴服務創建 一 個獨立的線程池, 這樣就算某個依賴服務出現延遲過高的情況, 也只是對該依賴服務的調用產生影響, 而不會拖慢其他的依賴服務。通過實現對依賴服務的線程池隔離, 可以帶來如下優勢:
- 應用自身得到完全保護, 不會受不可控的依賴服務影響。 即便給依賴服務分配的線程池被填滿, 也不會影響應用自身的其余部分。
- 可以有效降低接入新服務的風險。 如果新服務接入后運行不穩定或存在問題, 完全不會影響應用其他的請求。當依賴的服務從失效恢復正常后, 它的線程池會被清理並且能夠馬上恢復健康的服務, 相比之下, 容器級別的清理恢復速度要慢得多。
- 當依賴的服務出現配置錯誤的時候, 線程池會快速反映出此問題(通過失敗次數、延遲、超時、拒絕等指標的增加情況)。 同時, 我們可以在不影響應用功能的情況下通過實時的動態屬性刷新(后續會通過Spring Cloud Config與Spring Cloud Bus的聯合使用來介紹) 來處理它。
- 當依賴的服務因實現機制調整等原因造成其性能出現很大變化的時候, 線程池的監控指標信息會反映出這樣的變化。 同時, 我們也可以通過實時動態刷新自身應用對依賴服務的闕值進行調整以適應依賴方的改變。
- 除了上面通過線程池隔離服務發揮的優點之外, 每個專有線程池都提供了內置的並發實現, 可以利用它為同步的依賴服務構建異步訪問。
總之, 通過對依賴服務實現線程池隔離, 可讓我們的應用更加健壯, 不會因為個別依賴服務出現問題而引起非相關服務的異常。 同時, 也使得我們的應用變得更加靈活, 可以在不停止服務的情況下, 配合動態配置刷新實現性能配置上的調整。
6.HystrixObservableCommand.construct()或HystrixCommand.run():
Hystrix會根據我們編寫的方法來決定采取什么樣的方式去請求依賴服務。
- HystrixCommand.run(): 返回 一 個單 一 的結果,或者拋出異常。
- Hys 巨 ixObservableCommand.construct(): 返回 一 個Observable對象來發射多個結果,或通過onError發送錯誤通知。
如果run()或construet()方法的執行時間超過了命令設置的超時闕值,當前處理線程將會拋出 一 個TimeoutException (如果該命令不在其自身的線程中執行,則會通過單獨的計時線程來 拋出)。在這種情況下,Hystrix會轉接到fallback處理邏輯(第8步)。同時,如果當前命令沒有被取消或中斷, 那么它最終會忽略run()或者construct ()方法的返回。如果命令沒有拋出異常並返回了結果,那么Hystrix在記錄 一 些日志並采集監控報告之后將該結果返回。在使用run()的情況下,Hystrix會返回 一 個Observable, 它發射單個結果並產生onCompleted的結束通知; 而在使用construct ()的情況下,Hystrix會直接返回該方法產生的Observable對象。
7. 計算斷路器的健康度:
Hystrix會將“成功” 、 “失敗” 、 “ 拒絕 ” 、 “超時”等信息報告給斷路器,而斷路器會維護 一 組計數器來統計這些數據。斷路器會使用這些統計數據來決定是否要將斷路器打開,來對某個依賴服務的請求進行“熔斷/短路” ,直到恢復期結束。 若在恢復期結束后,根據統計數據判斷如果還是未達到健康指標,就再次“熔斷/短路” 。
8. fallback處理:
當命令執行失敗的時候, Hystrix會進入fallback嘗試回退處理, 我們通常也稱該操作為“服務降級 ” 。而能夠引起服務降級處理的清況有下面幾種:
- 第4步, 當前命令處於“熔斷I短路”狀態, 斷路器是打開的時候。
- 第5步, 當前命令的線程池、 請求隊列或者信號量被占滿的時候。
- 第6步,HystrixObservableCommand.construct()或HystrixCommand.run()拋出異常的時候。
在服務降級邏輯中, 我們需要實現一 個通用的響應結果, 並且該結果的處理邏輯應當是從緩存或是根據 一 些靜態邏輯來獲取,而不是依賴網絡請求獲取。如果一 定要在降級邏輯中包含網絡請求,那么該請求也必須被包裝在HystrixCommand或是HystrixObservableCommand中, 從而形成級聯的降級策略, 而最終的降級邏輯 一 定不是 一 個依賴網絡請求的處理, 而是 一 個能夠穩定地返回結果的處理邏輯。在HystrixCommand和HystrixObservableCommand中實現降級邏輯時還略有不同:
- 當使用HystrixCommand的時候, 通過實現HystrixCommand.getFallback()來實現服務降級邏輯。
- 當使用 HystrixObservableCommand 的時候, 通過 HystrixObservableCommand.resumeWithFallback()實現服務降級邏輯, 該方法會返回 一 個Observable對象來發射 一 個或多個降級結果。
當命令的降級邏輯返回 結果之后, Hystrix 就將該結果返回給調用者。 當使用HystrixCommand.getFallback()的時候, 它會返回一 個Observable對象, 該對象會 發 射 getFallback() 的 處 理 結 果 。 而使用 HystrixObservableCommand.resumeWithFallback ()實現的時候, 它會將Observable對象直接返回。如果我們沒有為命令實現降級邏輯或者降級處理邏輯中拋出了異常, Hystrix依然會返回 一 個Observable對象, 但是它不會發射任何結果數據, 而是通過onError 方法通知命令立即中斷請求,並通過onError()方法將引起命令失敗的異常發送給調用者。實現 一個有可能失敗的降級邏輯是 一 種非常糟糕的做法, 我們應該在實現降級策略時盡可能避免失敗的情況。
當然完全不可能出現失敗的完美策略是不存在的, 如果降級執行發現失敗的時候,Hystrix會根據不同的執行方法做出不同的處理。
- execute(): 拋出異常。
- queue(): 正常返回Future對象,但是當 調用get()來獲取結果的時候會拋出異常。
- observe () : 正常返回Observable對象, 當訂閱它的時候, 將立即通過調用訂閱者的onError方法來通知中止請求。
- toObservable(): 正常返回Observable對象, 當訂閱它的時候, 將通過調用訂閱者的onError方法來通知中止請求。
9. 返回成功的響應:
當Hystrix命令執行成功之后, 它會將處理結果直接返回或是以Observable 的形式返回。 而具體以哪種方式返回取決於之前第2步中我們所提到的對命令的4種不同執行方式, 下圖中總結了這4種 調用方式之間的依賴關系。 我們可以將此圖與在第2步中對前兩者源碼的分析聯系起來 , 並且從源頭toObservable()來開始分析。

- toObservable(): 返回最原始的 Observable, 必須通過訂閱它才會真正觸發命令的執行流程。
- observe () : 在toObservable()產生原始Observable 之后立即 訂閱它, 讓命令能夠馬上開始異步執行 , 並返回 一 個Observable 對象, 當調用它的subscribe 時, 將重新產生結果和通知給訂閱者。
- queue (): 將 toObservable()產生的原始Observable通過toBlocking()方法轉換成BlockingObservable對象, 並調用它的toFuture()方法 返回異步的Future對象。
- execute () : 在queue()產生異步結果Future對象之后,通過調用get()方法阻塞並等待結果的返回。
斷路器原理:
斷路器在 HystrixCommand 和 HystrixObservableCommand 執行過程中起到了舉足輕重的作用,它是 Hystrix 的核心部件。 那么斷路器是如何決策熔斷和記錄信息的呢?我們先來看看斷路器 HystrixCircuitBreaker 的定義:
public interface HystrixCircuitBreaker { /**每個 Hystrix 命令的請求都通過它判斷是否被執行。 * Every {@link HystrixCommand} requests asks this if it is allowed to proceed or not. * This takes into account the half-open logic which allows some requests through when determining if it should be closed again. * @return boolean whether a request should be permitted */
public boolean allowRequest(); /**返回當前斷路器是否打開。 * Whether the circuit is currently open (tripped). * @return boolean state of circuit breaker */
public boolean isOpen(); /**用來閉合斷路器。 * Invoked on successful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state. */
void markSuccess(); /**靜態類 Factory 中維護了 一 個 Hystrix 命令與 HystrixCircuitBreaker 的關系 * @ExcludeFromJavadoc * @ThreadSafe */
public static class Factory {...} /**斷路器接口 HystrixCircuitBreaker的實現類 * The default production implementation of {@link HystrixCircuitBreaker}. * @ExcludeFromJavadoc * @ThreadSafe */
static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {...} /**定義了一 個什么都不做的斷路器實現,它允許所有請求,並且斷路器狀態始終閉合。 * An implementation of the circuit breaker that does nothing. * @ExcludeFromJavadoc */
static class NoOpCircuitBreaker implements HystrixCircuitBreaker {...} }
HystrixCircuitBreakerImpl 對 HystrixCircuitBreaker 接口的各個方法實現如下所示。
isOpen (): 判斷斷路器的打開/關閉狀態。 詳細邏輯如下所示。
- 如果斷路器打開標識為true, 則直接返回true, 表示斷路器處千打開狀態。否則,就從度量指標對象 metrics 中獲取 HealthCounts 統計對象做進 一 步判斷(該對象記錄了一 個滾動時間窗內的請求信息快照,默認時間窗為10秒)。
- 如果它的請求總數(QPS)在預設的闕值范圍內就返回 false , 表示斷路器處於未打開狀態。該闕值的配置參數為 circuitBreakerRequestVolumeThreshold,默認值為20。
- 如果錯誤百分比在闌值范圍內就返回 false, 表示斷路器處於未打開狀態。該闕值的配置參數為 circuitBreakerErrorThresholdPercentage, 默認值為50 。
- 如果上面的兩個條件都不滿足,則將斷路器設置為打開狀態 (熔斷/短路)。 同時,如果是從關閉狀態切換到打開狀態的話,就將當前時間記錄到上面提到的circuitOpenedOrLastTestedTirne 對象中。
@Override public boolean isOpen() { if (circuitOpen.get()) { // if we're open we immediately return true and don't bother attempting to 'close' ourself as that is left to allowSingleTest and a subsequent successful test to close
return true; } // we're closed, so let's see if errors have made us so we should trip the circuit open
HealthCounts health = metrics.getHealthCounts(); // check if we are past the statisticalWindowVolumeThreshold
if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) { // we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anything
return false; } if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) { return false; } else { // our failure rate is too high, trip the circuit
if (circuitOpen.compareAndSet(false, true)) { // if the previousValue was false then we want to set the currentTime
circuitOpenedOrLastTestedTime.set(System.currentTimeMillis()); return true; } else { // How could previousValue be true? If another thread was going through this code at the same time a race-condition could have // caused another thread to set it to true already even though we were in the process of doing the same // In this case, we know the circuit is open, so let the other thread set the currentTime and report back that the circuit is open
return true; } } }
allowRequest(): 判斷請求是否被允許,這個實現非常簡單。 先根據配置對象properties中的斷路器判斷強制打開或關閉屬性是否被設置。 如果強制打開,就直接返回false, 拒絕請求。 如果強制關閉,它會允許所有請求,但是同時也會調用isOpen ()來執行斷路器的計算邏輯, 用來模擬斷路器打開/關閉的行為。 在默認情況下,斷路器並不會進入這兩個強制打開或關閉的分支中去,而是通過 !isOpen () II allowSingleTest ()來判斷是否允許請求訪問。 !isOpen()之前已經介紹過, 用來判斷和計算當前斷路器是否打開,如果是斷開狀態就允許請求。 那么allowSingleTest()是用來做什么的呢?
@Override public boolean allowRequest() { if (properties.circuitBreakerForceOpen().get()) { // properties have asked us to force the circuit open so we will allow NO requests
return false; } if (properties.circuitBreakerForceClosed().get()) { // we still want to allow isOpen() to perform it's calculations so we simulate normal behavior
isOpen(); // properties have asked us to ignore errors so we will ignore the results of isOpen and just allow all traffic through
return true; } return !isOpen() || allowSingleTest(); }
從allowSingleTest()的實現中我們可以看到,這里使用了在isOpen()函數中當斷路器從閉合到打開時候所記錄的時間戳。 當斷路器在打開狀態的時候,這里會判斷斷開時的時間戳+配置中的circuitBreakerSleepWndowinMilliseconds時間是否小於當前時間,是的話,就將當前時間更新到記錄斷路器打開的時間對象 circuitOpenedOrLastTestedTime 中,並且允許此次請求。 簡單地說, 通過 circuitBreakerSleepWindowinMilliseconds 屬性設置了一 個斷路器打開之后的休眠時間(默認為5秒),在該休眠時間到達之后,將再次允許請求嘗試訪問,此時斷路器處於“半開”狀態,若此時請求繼續失敗, 斷路器又進入打開狀態, 並繼續等待下 一 個休眠窗口過去之后再次嘗試;若請求成功, 則將斷路器重新置於關閉狀態。所以通過 allowSingleTest()與isOpen ()方法的配合,實現了斷路器打開和關閉狀態的切換。
public boolean allowSingleTest() { long timeCircuitOpenedOrWasLastTested = circuitOpenedOrLastTestedTime.get(); // 1) if the circuit is open // 2) and it's been longer than 'sleepWindow' since we opened the circuit
if (circuitOpen.get() && System.currentTimeMillis() > timeCircuitOpenedOrWasLastTested + properties.circuitBreakerSleepWindowInMilliseconds().get()) { // We push the 'circuitOpenedTime' ahead by 'sleepWindow' since we have allowed one request to try. // If it succeeds the circuit will be closed, otherwise another singleTest will be allowed at the end of the 'sleepWindow'.
if (circuitOpenedOrLastTestedTime.compareAndSet(timeCircuitOpenedOrWasLastTested, System.currentTimeMillis())) { // if this returns true that means we set the time so we'll return true to allow the singleTest // if it returned false it means another thread raced us and allowed the singleTest before we did
return true; } } return false; }
markSuccess(): 該函數用來在“半開路”狀態時使用。若 Hystrix 命令調用成功,通過調用它將打開的斷路器關閉, 並重置度量指標對象。
public void markSuccess() { if (circuitOpen.get()) { if (circuitOpen.compareAndSet(true, false)) { //win the thread race to reset metrics //Unsubscribe from the current stream to reset the health counts stream. This only affects the health counts view, //and all other metric consumers are unaffected by the reset
metrics.resetStream(); } } }
下圖是 Netflix Hystrix 官方文檔中關千斷路器的詳細執行邏輯,可以幫助我們理解上面的分析內容。

使用詳解:
上文中我們通過Hystrix 中的核心注解 @HystrixCommand, 通過它創建了 HystrixCommand 的實現,同時利用 fallback 屬性指定了服務降級的實現方法。然而這些還只是 Hystrix 使用的一 小部分,在實現 一 個大型分布式系統時,往往還需要更多高級的配置功能。 接下來我們將詳細介紹 Hystrix 各接口和注解的使用方法。創建請求命令:
Hystrix 命令就是我們之前所說的 HystrixCommand, 它用來封裝具體的依賴服務調用邏輯。我們可以通過繼承的方式來實現, 比如:
public class HelloCommand extends HystrixCommand<String> { private RestTemplate restTemplate; private HashMap map; public HelloCommand(RestTemplate restTemplate, HashMap paramMap) { super(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey( HystrixCommandGroupKey.Factory.asKey("")).andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000))); this.restTemplate = restTemplate; this.map = paramMap; } @Override protected String run() { return restTemplate.getForObject("http://cloud-provider/hello?id={id}", String.class, map); } // 服務降級 @Override protected String getFallback() { return "error-err"; } }
通過上面實現的HelloCommand , 我們既可以實現請求的同步執行也可以實現異步執行。除了傳統的同步執行與異步執行之外, 我們還可以將 HystrixComrnand 通過Observable 來實現響應式執行方式。通過調用 observe()和toObservable ()方法可以返回 Observable 對象observe ()和toObservable ()雖然都返回了 Observable, 但是它們略有不同,前者返回的是一 個Hot Observable, 該命令會在 observe ()調用的時候立即執行, 當Observable 每次被訂閱的時候會重放它的行為;而后者返回的是一 個Cold Observable,toObservable ()執行之后,命令不會被立即執行,只有當所有訂閱者都訂閱它之后才會執行。
//繼承HystrixCommand的實現
@RequestMapping(value = "/helloCommand") public String helloCommand(Long id) { HashMap map = new HashMap<>(); map.put("id", id); //同步
String result = new HelloCommand(restTemplate, map).execute(); //異步 // Future<String> result = new HelloCommand(restTemplate, map).queue(); //響應式執行方式 // Observable<String> hotObserve = new HelloCommand(restTemplate, map).observe(); // Observable<String> coldObservable = new HelloCommand(restTemplate, map).toObservable();
return result; }
異步執行的時候, 可以通過對返回的 result 調用 get 方法來獲取結果。另外, 也可以通過 上文@HystrixCommand 注解來更為優雅地實現 Hystrix 命令的定義,雖然 @HystrixCommand 注解可以非常優雅地定義 Hystrix 命令的實現, 但是如上定義的 get 方式只是同步執行的實現,若要實現異步執行則還需另外定義,比如:
//異步
@HystrixCommand(fallbackMethod = "getByidAsyncFailed")//熔斷機制
@RequestMapping(value = "/getByidAsync") public String getUserByidAsync(String id) { HashMap map = new HashMap<>(); map.put("id", id); AsyncResult<String> asyncResult = new AsyncResult<String>() { @Override public String invoke() { return restTemplate.getForObject(REST_URL_PREFIX + "/hello?id={id}", String.class, map); } @Override public String get() { return invoke(); } }; return asyncResult.get(); }
雖然 HystrixCornrnand 具備了 observe ()和toObservable() 的功能,但是它的實現有 一 定的局限性,它返回的 Observable 只能發射 一 次數據,所以 Hystrix 還提供了另外 一 個特殊命令封裝 HystrixObservableCornrnand, 通過它實現的命令可以獲取能發射多次的 Observable 。如果使用 HystrixObservableCornrnand 來實現命令封裝,需要將命令的執行邏輯在construct 方法中重載,這樣 Hystrix 才能將具體邏輯包裝到 Observable 內,如下所示:
public class HelloObservableCommand extends HystrixObservableCommand<String> { private RestTemplate restTemplate; private HashMap map; public HelloObservableCommand(RestTemplate restTemplate, HashMap paramMap) { super(com.netflix.hystrix.HystrixObservableCommand.Setter.withGroupKey( HystrixCommandGroupKey.Factory.asKey("")).andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000))); this.restTemplate = restTemplate; this.map = paramMap; } @Override protected Observable<String> construct() { return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> observer) { try { if (!observer.isUnsubscribed()) { String string = restTemplate.getForObject("http://cloud-provider/hello?id={id}", String.class, map); observer.onNext(string); observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } }); } }
而對此的注解實現依然是使用 @HystrixCommand, 只是方法定義需要做 一 些變化,具體內容與 construct ()的實現類似,如下所示:
//HystrixObservableCommand //EAGER 是該參數的模式值, 表示使用 observe ()執行方式。
@HystrixCommand(fallbackMethod = "getByidAsyncFailed", observableExecutionMode = ObservableExecutionMode.EAGER) // //表示使用 toObservable() 執行方式。 // @HystrixCommand(fallbackMethod = "getByidAsyncFailed",observableExecutionMode = ObservableExecutionMode.LAZY)
@RequestMapping(value = "/helloHystrixObservableCommand") public Observable<String> helloHystrixObservableCommand(String id) { HashMap map = new HashMap<>(); map.put("id", id); return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> observer) { try { if (!observer.isUnsubscribed()) { String string = restTemplate.getForObject("http://cloud-provider/hello?id={id}", String.class, map); observer.onNext(string); observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } }); }
異常處理:
異常傳播:
在 HystrixComrnand 實現的 run() 方法中拋出異常時, 除了 HystrixBadRequestException 之外,其他異常均會被 Hystrix 認為命令執行失敗並觸發服務降級的處理邏輯,所以當需要在命令執行中拋出不觸發服務降級的異常時來使用它。而在使用注冊配置實現 Hystrix 命令時,它還支持忽略指定異常類型功能, 只需要通過設置 @HystrixComrnand 注解的 ignoreExceptions 參數, 比如:@HystrixCommand(ignoreExceptions = {BadReques七Exception.class}).當方法拋出了類型為 BadRequestExcep巨on的異常時, Hystrix 會將它包裝在 HystrixBadRequestException 中拋出, 這樣就不會觸發后續的 fallback 邏輯。
異常獲取:
當 Hystrix 命令因為異常(除了 HystrixBadRequestException 的異常)進入服務降級邏輯之后, 往往需要對不同異常做針對性的處理, 那么我們如何來獲取當前拋出的異常呢?在以傳統繼承方式實現的 Hystrix 命令中, 我們可以用 getFallback ()方法通過 getExecutionException() 方法來獲取具體的異常, 通過判斷來進入不同的處理邏輯。
除了傳統的實現方式之外,注解配置方式也同樣可以實現異常的獲取。 它的實現也非常簡單, 只需要在 fallback 實現方法的參數中增加 Throwable e 對象的定義, 這樣在方法內部就可以獲取觸發服務降級的具體異常內容了, 比如:fallbackl(Throwable e)
請求緩存:
在高並發的場景之下, Hystrix 中提供了請求緩存的功能, 我們可以方便地開啟和使用請求緩存來優化系統, 達到減輕高並發時的請求線程消耗、 降低請求響應時間的效果。
開啟請求緩存功能 :Hystrix 請求緩存的使用非常簡單, 我們只需要在實現 HystrixCommand 或 HystrixObservableCommand 時, 通過重載 getCacheKey ()方法來開啟請求緩存。通過開啟請求緩存可以讓我們實現的 Hystrix 命令具備下面幾項好處:
- 減少重復的請求數, 降低依賴服務的並發度。
- 在同一 用戶請求的上下文中, 相同依賴服務的返回數據始終保持 一 致。
- 請求緩存在 run() 和 cons七ruct ()執行之前生效, 所以可以有效減少不必要的線程開銷。
清理失效緩存功能
清除緩存有兩個方式
//刷新緩存,根據id進行清理 自己寫一個flush方法。通過idzuoweikey清除 HystrixRequestCache.getInstance(GETTER_KEY,HystrixConcurrencyStrategyDefault.getinstance()).clear(String.valueOf(id)); //刷新緩存, 清理緩存中失效的User,直接調用flush方法 HelloCommand.flushCache(id);
請求合並:
微服務架構中的依賴通常通過遠程調用實現, 而遠程調用中最常見的問題就是通信消耗與連接數占用。 在高並發的情況之下, 因通信次數的增加, 總的通信時間消耗將會變得不那么理想。 同時, 因為依賴服務的線程池資源有限,將出現排隊等待與響應延遲的清況。為了優化這兩個問題, Hystrix 提供了 HystrixCollapser 來實現請求的合並,以減少通信消耗和線程數的占用。
HystrixCollapser 實現 了在 HystrixCommand 之前放置 一 個合並處理器, 將處於一個很短的時間窗(默認 10 毫秒)內對同 一 依賴服務的多個請求進行整合並以批量方式發起請 求 的功能(服 務提供方也需 要 提供相應的批 量實 現 接口)。 通 過HystrixCollapser 的封裝, 開發者不需要關注線程合並的細節過程, 只需關注批量化服務和處理。 下面我們從 HystrixCollapser 的使用實例 中對其合並請求的過程 一 探究竟。
public abstract class HystrixCollapser<BatchReturnType, ResponseType, RequestArgumentType> implements HystrixExecutable<ResponseType>, HystrixObservable<ResponseType> {
//BatchReturnType: 合並后批量請求的返回類型。 // ResponseType: 單個請求返回的類型。 //RequestArgumentType: 請求參數類型。
//該函數用來定義獲取請求參數的方法。
public abstract RequestArgumentType getRequestArgument();
//合並請求產生批量命令的具體實現方法。
protected abstract HystrixCommand<BatchReturnType> createCommand(Collection<CollapsedRequest<ResponseType, RequestArgumentType>> requests);
//批量命令結果返回后 的處理, 這里需要實現將批量結果拆分並傳遞給合並前的各個原子請求命令的邏輯。
protected abstract void mapResponseToRequests(BatchReturnType batchResponse, Collection<CollapsedRequest<ResponseType, RequestArgumentType>> requests); }
接下來, 我們通過 一 個簡單的示例來直觀理解實現請求合並的過程。
首先在我們原來的服務提供者 cloud-provider工程中加入一個批量獲取的接口,那么現在兩個接口如下:
@GetMapping("/hello") public String helloEureka(){ return "Hello Eureka Provider1"; } @GetMapping("/hi") public List<String> hi(String ids) { //ids是 , 隔開的字符串
String[] split = ids.split(","); ArrayList<String> objects = new ArrayList<String>(); for(String s:split){ objects.add("hi! wuzz:ID: " + s); } return objects; }
創建一個獨立的消費者服務,用於通過 RestTemplate 實現了簡單的調用
@Service public class HelloCollapseService { @Autowired private RestTemplate restTemplate; private static final String REST_URL_PREFIX = "http://cloud-provider"; //同步
public String hello(String id) { return restTemplate.getForObject(REST_URL_PREFIX + "/hello/{1}", String.class, id); } //同步
public List<String> hi(List<String> ids) { String[] forObject = restTemplate.getForObject(REST_URL_PREFIX + "/hi?ids={1}", String[].class, StringUtils.join(ids, ",")); return Arrays.asList(forObject); } }
接着, 我們實現將短時間內多個獲取單一對象的請求命令進行合並。第 一 步,為請求合並的實現准備 一 個批量請求命令的實現, 具體如下:
//為請求合並的實現准備 一 個批量請求命令的實現 //批量請求命令實際上就是 一 個簡單的HystrixCommand實現
public class HelloBatchCommand extends HystrixCommand<List<String>> { private HelloCollapseService helloCollapseService; private List<String> ids; public HelloBatchCommand(HelloCollapseService helloCollapseService, List<String> ids) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("helloBatchCommand"))); this.helloCollapseService = helloCollapseService; this.ids = ids; } @Override protected List<String> run() { //這段打印用域等等測試,查看是否是調用這個接口去服務獲取數據的
System.out.println("finaAll request:---------" + ids + "Thread.currentThread().getName():-------" + Thread.currentThread().getName()); return helloCollapseService.hi(ids); } @Override protected List<String> getFallback() { List<String> users = new ArrayList<String>(); users.add("失敗者"); return new ArrayList<String>(users); } }
批量請求命令實際上就是 一 個簡單的HystrixCommand實現,從上面的實現中可以看到它通過調用 helloCollapseService.hi(ids) 來批量獲取結果。
第二步, 通過繼承HystrixCollapser實現請求合並器,關於這個類的定義以及需要實現的方法已經在上面說明:
//通過繼承HystrixCollapser實現請求合並器
public class HelloCollapseCommand extends HystrixCollapser<List<String>, String, String> { private HelloCollapseService helloCollapseService; private String id; public HelloCollapseCommand(HelloCollapseService helloCollapseService, String id) { super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("helloCollapseCommand")) .andCollapserPropertiesDefaults( HystrixCollapserProperties.Setter() .withTimerDelayInMilliseconds(100))); this.helloCollapseService = helloCollapseService; this.id = id; } @Override public String getRequestArgument() { return id; } @Override protected HystrixCommand<List<String>> createCommand(Collection<CollapsedRequest<String, String>> collapsedRequests) { List<String> userids = new ArrayList<>(collapsedRequests.size()); userids.addAll(collapsedRequests.stream().map(CollapsedRequest::getArgument).collect(Collectors.toList())); return new HelloBatchCommand(helloCollapseService, userids); } @Override protected void mapResponseToRequests(List<String> batchResponse, Collection<CollapsedRequest<String, String>> collapsedRequests) { int count = 0; for (CollapsedRequest<String, String> collapsedRequest : collapsedRequests) { String user = batchResponse.get(count++); collapsedRequest.setResponse(user); } } }
最后創建測試類,從以下這個測試方法可以看出,我們想要的結果是一共發送了兩次請求,一次是6、5、9作為批量的請求。由於程序sleep了 3秒,而我們設置的時間間隔為1秒,所以這里8這個ID的請求會單獨發送:
@RequestMapping(value = "/batchHello") public List<String> batchHello() throws InterruptedException, ExecutionException { //需要開啟HystrixRequest上下文,合並請求和緩存必須開啟
HystrixRequestContext context = HystrixRequestContext.initializeContext(); List<String> result = new ArrayList<>(); HelloCollapseCommand bc1 = new HelloCollapseCommand(helloCollapseService, "6"); HelloCollapseCommand bc2 = new HelloCollapseCommand(helloCollapseService, "9"); HelloCollapseCommand bc3 = new HelloCollapseCommand(helloCollapseService, "5"); HelloCollapseCommand bc4 = new HelloCollapseCommand(helloCollapseService, "8"); Future<String> q1 = bc1.queue(); Future<String> q2 = bc2.queue(); Future<String> q3 = bc3.queue(); String result1 = q1.get(); String result2 = q2.get(); String result3 = q3.get(); Thread.sleep(3000); Future<String> q4 = bc4.queue(); String result4 = q4.get(); return result; }
啟動服務調用 http://localhost:9001/batchHello ,然后查看控制台,可以看到結果是我們所預期的:

注解的方式實現請求合並:
在原來的 HelloCollapseService 上做改動,增加find、findAll方法如下:
@Service public class HelloCollapseService { @Autowired private RestTemplate restTemplate; private static final String REST_URL_PREFIX = "http://cloud-provider"; //同步
public String hello(String id) { return restTemplate.getForObject(REST_URL_PREFIX + "/hello/{1}", String.class, id); } //同步
public List<String> hi(List<String> ids) { String[] forObject = restTemplate.getForObject(REST_URL_PREFIX + "/hi?ids={1}", String[].class, StringUtils.join(ids, ",")); return Arrays.asList(forObject); } @HystrixCollapser(batchMethod = "findAll", collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "100")}) public Future<String> find(String id) { throw new RuntimeException("This method body should not be executed"); } @HystrixCommand(fallbackMethod = "annotationBatchHelloBack") public List<String> findAll(List<String> ids) { System.out.println("Annotation---------" + ids + "Thread.currentThread().getName():" + Thread.currentThread().getName()); String[] users = restTemplate.getForObject(REST_URL_PREFIX + "/hi?ids={1}", String[].class, StringUtils.join(ids, ",")); return Arrays.asList(users); } public List<String> annotationBatchHelloBack(List<Long> ids) { return Arrays.asList("annotationBatchHelloBack Hystrix" +ids); } }
增加測試方法:
@RequestMapping(value = "/annotationBatchHello") public String find(String id) throws ExecutionException, InterruptedException { HystrixRequestContext context = HystrixRequestContext.initializeContext(); Future<String> stringFuture = helloCollapseService.find(id); Future<String> stringFuture2 = helloCollapseService.find("6"); return stringFuture.get()+"======"+stringFuture2.get(); }
這個時候我訪問的路徑變為 http://localhost:9001/annotationBatchHello?id=88 應該看到的結果是 88 +6 兩個ID組成一個批量請求發送,如下圖所示:

下圖展示了在未使用HystrixCollapser請求合並器之前的線程使用情況。 可以看到, 當服務消費者同時對RIBBON-SERVER的 /hello/{id}接口發起了5個請求時, 會向該依賴服務的獨立線程池中申請5個線程來完成各自的請求操作。

而在使用了HystrixCollapser請求合並器之后, 相同情況下的線程占用如下圖所示。由於同一 時間發生的5個請求處於請求合並器的 一 個時間窗內,這些發向/hello/{id}接口的請求被請求合並器攔截下來, 並在合並器中進行組合, 然后將這些請求合並成 一 個請求發向 CLOUD-PROVIDER 的批量接口 /hi/{ids} 。在獲取到批量請求結果之后,通過請求合並器再將批量結果拆分並分配給每個被合並的請求。 從圖中我們可以看到, 通過使用請求合並器有效減少了對線程池中資源的占用。 所以在資源有效並且短時間內會產生高並發請求的時候, 為避免連接不夠用而引起的延遲可以考慮使用請求合並器的方式來處理和優化。

請求合並的額外開銷:
雖然通過請求合並可以減少請求的數量以緩解依賴服務線程池的資源, 但是在使用的時候也需要注意它所帶來的額外開銷: 用於請求合並的延遲時間窗會使得依賴服務的請求延遲增高。 比如, 某個請求不通過請求合並器訪問的平均耗時為5ms, 請求合並的延遲時間窗為10ms (默認值), 那么當該請求設置了請求合並器之后, 最壞情況下(在延遲時間窗結束時才發起請求)該請求需要 15ms才能完成。由於請求合並器的延遲時間窗會帶來額外開銷, 所以我們是否使用請求合並器需要 根據依賴服務調用的實際情況來選擇, 主要考慮下面兩個方面。
- 請求命令本身的延遲。 如果依賴服務的請求命令本身是 一 個高延遲的命令, 那么可以使用請求合並器, 因為延遲時間窗的時間消耗顯得微不足道了。
- 延遲時間窗內的並發量。 如果 一 個時間窗內只有1-2個請求, 那么這樣的依賴服務不適合使用請求合並器。 這種情況不但不能提升系統性能, 反而會成為系統瓶頸,因為每個請求都需要多消耗 一 個時間窗才響應。 相反, 如果 一 個時間窗內具有很高的並發量, 並且服務提供方也實現了批量處理接口, 那么使用請求合並器可以有效減少網絡連接數量並極大提升系統吞吐量, 此時延遲時間窗所增加的消耗就可以忽略不計了。
屬性詳解:
我們可以根據實現 HystrixCommand 的不同方式將配置方法分為如下兩類。
當通過繼承的方式實現時, 可使用 Setter 對象來對請求命令的屬性進行設置, 比如下面的例子:
public HelloCommand(RestTemplate restTemplate, HashMap paramMap) { super(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey( HystrixCommandGroupKey.Factory.asKey("helloCommand")).andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000))); this.restTemplate = restTemplate; this.map = paramMap; }
當通過注解的方法實現時, 只需使用 @HystrixCommand 中的 command Properties 屬性來設置, 比如:
@HystrixCollapser(batchMethod = "findAll", collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "100")})
具體的參數配置還可以分為
- Command屬性:主要用來控制HystrixCommand命令的行為。包括隔離策略配置、超時時間配置等。
- fallback配置:用來控制HystrixComrnand.getFallback ()的執行。 這些屬性同時適用於線程池的信號量的隔離策略。包括getFallback 方法執行的最大並發數、服務降級策略是否啟用等。
- circuitBreaker配置:用來控制HystrixCircuitBreaker的行為。包括熔斷最小請求數、休眠時間窗等。
- metrics配置:與 HystrixCommand 和 HystrixObservableCommand 執行中捕獲的指標信息有關。包括滾動時間窗的長度、滾動時間窗統計指標信息時划分“桶”的數量等
- requestContext配置:涉及HystrixCommand使用的HystrixRequestContext的設置。包括是否開啟請求緩存,是否執行和事件記錄到日志等。
- collapser屬性:用來控制命令合並相關的行為。包括一 次請求合並批處理中允許的最大請求數、每個命令延遲的時間等。
- threadPool屬性:用來控制Hy strix命令所屬線程池的配置。包括執行命令線程池的核心線程數,該值也就是命令執行的最大並發量等。
更加詳細的信息可以參閱 《Spring Cloud微服務實戰》。
