對Hystrix每個步驟的實現原理分析
1. 構建一個HystrixCommand或者HystrixObservableCommand
一個HystrixCommand或一個HystrixObservableCommand對象,代表了對某個依賴服務發起的一次請求或者調用。構造的時候,可以在構造函數中傳入任何需要的參數。HystrixCommand主要用於僅僅會返回一個結果的調用;HystrixObservableCommand主要用於可能會返回多條結果的調用
HystrixCommand command = new HystrixCommand(arg1, arg2);
HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);
2. 調用command的執行方法
要執行Command,需要在4個方法中選擇其中的一個:execute(),queue(),observe(),toObservable()。其中execute()和queue()僅僅對HystrixCommand適用
- execute():調用後直接block住,屬於同步調用,直到依賴服務返回單條結果,或者拋出異常
- queue():返回一個Future,屬於異步調用,後面可以通過Future獲取單條結果
- observe():訂閱一個Observable對象,Observable代表的是依賴服務返回的結果,獲取到一個那個代表結果的 Observable對象的拷貝對象
- toObservable():返回一個Observable對象,如果我們訂閱這個對象,就會執行command並且獲取返回結果
K value = command.execute();
Future<K> fValue = command.queue();
Observable<K> ohValue = command.observe();
Observable<K> ocValue = command.toObservable();
3. 檢查是否開啓緩存
如果這個command開啓了請求緩存,request cache,而且這個調用的結果在緩存中存在,那麼直接從緩存中返回結果。一般來說,所謂的request cache只是針對一次request context,類似於web應用中響應一個請求去調用多個依賴服務,對同一個依賴的相同參數的調用可以放到緩存從而減少網絡請求來提升性能。具體實現可以通過添加過濾器來添加一個HystrixRequestContext
public class HystrixRequestContextServletFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
chain.doFilter(request, response);
} finally {
context.shutdown();
}
}
}
在Command實現中有CacheKey的設定方法:
public class CommandUsingRequestCache extends HystrixCommand<Boolean> {
...
@Override
protected String getCacheKey() {
return String.valueOf(value);
}
}
4.檢查是否開啓了短路器
檢查這個command對應的依賴服務是否開啓了短路器。如果斷路器被打開了,那麼hystrix就不會執行這個command,而是直接去執行fallback降級機制。
斷路器的實現原理:
控制短路器是否允許工作,包括跟蹤依賴服務調用的健康狀況,以及對異常情況過多時是否允許觸發短路,默認是true,一般不需要修改
HystrixCommandProperties.Setter() .withCircuitBreakerEnabled(boolean value)
只要執行一個command,這個請求就一定會經過短路器,如果在一定時間內經過短路器的流量超過了一定的閾值,纔會進行後面的斷路器相關處理。可以通過以下配置修改,默認值是20
HystrixCommandProperties.Setter() .withCircuitBreakerRequestVolumeThreshold(int value)
如果斷路器統計到的異常調用的佔比超過了一定的閾值,纔會打開斷路器開關。默認是50%的異常比例
HystrixCommandProperties.Setter() .withCircuitBreakerErrorThresholdPercentage(int value)
經過以上步驟,然後斷路器從close狀態轉換到open狀態
- 斷路器打開的時候,所有經過該斷路器的請求全部被短路,不調用後端服務,直接走fallback降級
經過了一段時間之後,斷路器會進入half-open狀態,讓一條請求經過短路器,看能不能正常調用。如果調用成功了,那麼就自動恢復,轉到close狀態。時間可以通過以下配置來修改,默認爲5000毫秒
HystrixCommandProperties.Setter() .withCircuitBreakerSleepWindowInMilliseconds(int value)
可以強迫打開短路器,一般不使用
HystrixCommandProperties.Setter() .withCircuitBreakerForceOpen(boolean value)
強迫關閉短路器,一般不使用
HystrixCommandProperties.Setter() .withCircuitBreakerForceClosed(boolean value)
5.檢查線程池/隊列/semaphore是否已經滿了
如果command對應的線程池/隊列/semaphore已經滿了,那麼也不會執行command,而是直接去調用fallback降級機制
6.執行command
調用HystrixObservableCommand.construct()或HystrixCommand.run()來實際執行這個command
- HystrixCommand.run()是返回一個單條結果,或者拋出一個異常
- HystrixObservableCommand.construct()是返回一個Observable對象,可以獲取多條結果
如果HystrixCommand.run()或HystrixObservableCommand.construct()的執行,超過了timeout時長的話,那麼command所在的線程就會拋出一個TimeoutException。如果timeout了,也會去執行fallback降級機制,而且就不會管run()或construct()返回的值了。這裏要注意的一點是,我們是不可能終止掉一個調用嚴重延遲的依賴服務的線程的,只能說給你拋出來一個TimeoutException,但是還是可能會因爲嚴重延遲的調用線程佔滿整個線程池的,即使這個時候新來的流量都被限流了。如果沒有timeout的話,那麼就會拿到一些調用依賴服務獲取到的結果,然後hystrix會做一些logging記錄和metric統計。
有一個很重要的點,command的執行強烈建議我們設置一個timeout的時間,來避免所有資源都被佔用導致系統整體性能下降,可以通過以下來配置:
//默認是true打開超時控制
HystrixCommandProperties.Setter()
.withExecutionTimeoutEnabled(boolean value)
//默認1000ms
HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(int value)
當command執行超時之後會直接進行fallback降級處理
7.短路健康檢查
Hystrix會將每一個依賴服務的調用成功,失敗,拒絕,超時,等事件,都會發送給circuit breaker斷路器。短路器就會對調用成功/失敗/拒絕/超時等事件的次數進行統計。短路器會根據這些統計次數來決定,是否要進行短路,如果打開了短路器,那麼在一段時間內就會直接短路,然後如果在之後第一次檢查發現調用成功了,就關閉斷路器
8.調用fallback降級機制
一般來說有四種情況都會去調用fallback降級機制:
- hystrix調用各種外部接口,或者訪問外部依賴,mysql,redis,zookeeper,kafka,等等,出現了任何異常的情況
- 對外部的依賴調用所使用的線程池已滿,或者使用信號量限流資源到達極限
- 訪問時間過長,可能就會導致超時,報一個TimeoutException異常
- 基於上述三種情況都會發送異常事件到斷路器中去進行統計,如果異常達到一定的比例直接開啓短路circuit breaker
兩種常見的降級處理是,維護內存ecache直接獲取一份過期的數據,另一種方案是設定一個默認值返回
public class CommandHelloFailure extends HystrixCommand<String> {
private final String name;
public CommandHelloFailure(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected String run() {
throw new RuntimeException("this command always fails");
}
@Override
protected String getFallback() {
return "Hello Failure " + name + "!";
}
}
HystrixObservableCommand,是實現resumeWithFallback方法
一般在降級機制中,都建議給出一些默認的返回值,比如靜態的一些代碼邏輯,或者從內存中的緩存中提取一些數據,儘量在這裏不要再進行網絡請求了。即使在降級中,一定要進行網絡調用,也應該將那個調用放在一個HystrixCommand中,進行隔離
設置fallback.isolation.semaphore.maxConcurrentRequests,這個參數設置了HystrixCommand.getFallback()最大允許的併發請求數量,默認值是10,也是通過semaphore信號量的機制去限流。如果超出了這個最大值,那麼直接被reject
HystrixCommandProperties.Setter().withFallbackIsolationSemaphoreMaxConcurrentRequests(int value)