Hystrix是Netflix開源的一款容錯系統,能幫助使用者碼出具有強大的容錯能力和魯棒性的程序。若是某程序或class要使用Hystrix,只需簡單繼承HystrixCommand/HystrixObservableCommand
並重寫run()/construct()
,而後調用程序實例化此class並執行execute()/queue()/observe()/toObservable()
。git
// HelloWorldHystrixCommand要使用Hystrix功能 public class HelloWorldHystrixCommand extends HystrixCommand { private final String name; public HelloWorldHystrixCommand(String name) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.name = name; } // 若是繼承的是HystrixObservableCommand,要重寫Observable construct() @Override protected String run() { return "Hello " + name; } }
/* 調用程序對HelloWorldHystrixCommand實例化,執行execute()即觸發HelloWorldHystrixCommand.run()的執行 */ String result = new HelloWorldHystrixCommand("HLX").execute(); System.out.println(result); // 打印出Hello HLX
pom.xml加上如下依賴。spring cloud也集成了hystrix,不過本文只介紹原生hystrix。github
<dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-core</artifactId> <version>1.5.8</version> </dependency>
本文重點介紹的是Hystrix各項基礎能力的用法及其效果,不從零介紹hystrix,要了解基礎知識推薦官網wiki或民間blogspring
要想使用hystrix,只須要繼承HystrixCommand
或HystrixObservableCommand
,簡單用法見上面例子。二者主要區別是:緩存
前者的命令邏輯寫在run()
;後者的命令邏輯寫在construct()
網絡
前者的run()
是由新建立的線程執行;後者的construct()
是由調用程序線程執行併發
前者一個實例只能向調用程序發送(emit)單條數據,好比上面例子中run()
只能返回一個String結果;後者一個實例能夠順序發送多條數據,好比demo中順序調用多個onNext()
,便實現了向調用程序發送多條數據,甚至還能發送一個範圍的數據集異步
execute()
、queue()
、observe()
、toObservable()
這4個方法用來觸發執行run()/construct()
,一個實例只能執行一次這4個方法,特別說明的是HystrixObservableCommand
沒有execute()
和queue()
。ide
4個方法的主要區別是:測試
execute()
:以同步堵塞方式執行run()
。以demo爲例,調用execute()
後,hystrix先建立一個新線程運行run()
,接着調用程序要在execute()
調用處一直堵塞着,直到run()
運行完成ui
queue()
:以異步非堵塞方式執行run()
。以demo爲例,一調用queue()
就直接返回一個Future對象,同時hystrix建立一個新線程運行run()
,調用程序經過Future.get()
拿到run()
的返回結果,而Future.get()
是堵塞執行的
observe()
:事件註冊前執行run()/construct()
。以demo爲例,第一步是事件註冊前,先調用observe()
自動觸發執行run()/construct()
(若是繼承的是HystrixCommand
,hystrix將建立新線程非堵塞執行run()
;若是繼承的是HystrixObservableCommand
,將以調用程序線程堵塞執行construct()
),第二步是從observe()
返回後調用程序調用subscribe()
完成事件註冊,若是run()/construct()
執行成功則觸發onNext()
和onCompleted()
,若是執行異常則觸發onError()
toObservable()
:事件註冊後執行run()/construct()
。以demo爲例,第一步是事件註冊前,一調用toObservable()
就直接返回一個Observable<String>
對象,第二步調用subscribe()
完成事件註冊後自動觸發執行run()/construct()
(若是繼承的是HystrixCommand
,hystrix將建立新線程非堵塞執行run()
,調用程序沒必要等待run()
;若是繼承的是HystrixObservableCommand
,將以調用程序線程堵塞執行construct()
,調用程序等待construct()
執行完才能繼續往下走),若是run()/construct()
執行成功則觸發onNext()
和onCompleted()
,若是執行異常則觸發onError()
使用fallback機制很簡單,繼承HystrixCommand
只需重寫getFallback()
,繼承HystrixObservableCommand
只需重寫resumeWithFallback()
,好比HelloWorldHystrixCommand
加上下面代碼片斷:
@Override protected String getFallback() { return "fallback: " + name; }
fallback實際流程是當run()/construct()
被觸發執行時或執行中發生錯誤時,將轉向執行getFallback()/resumeWithFallback()
。結合下圖,4種錯誤狀況將觸發fallback:
非HystrixBadRequestException異常:以demo爲例,當拋出HystrixBadRequestException時,調用程序能夠捕獲異常,沒有觸發getFallback()
,而其餘異常則會觸發getFallback()
,調用程序將得到getFallback()
的返回
run()/construct()
運行超時:以demo爲例,使用無限while循環或sleep模擬超時,觸發了getFallback()
熔斷器啓動:以demo爲例,咱們配置10s內請求數大於3個時就啓動熔斷器,請求錯誤率大於80%時就熔斷,而後for循環發起請求,當請求符合熔斷條件時將觸發getFallback()
。更多熔斷策略見下文
線程池/信號量已滿:以demo爲例,咱們配置線程池數目爲3,而後先用一個for循環執行queue()
,觸發的run()
sleep 2s,而後再用第2個for循環執行execute()
,發現全部execute()
都觸發了fallback,這是由於第1個for的線程還在sleep,佔用着線程池全部線程,致使第2個for的全部命令都沒法獲取到線程
調用程序能夠經過
isResponseFromFallback()
查詢結果是由run()/construct()
仍是getFallback()/resumeWithFallback()
返回的
hystrix提供了兩種隔離策略:線程池隔離和信號量隔離。hystrix默認採用線程池隔離。
線程池隔離:不一樣服務經過使用不一樣線程池,彼此間將不受影響,達到隔離效果。以demo爲例,咱們經過andThreadPoolKey配置使用命名爲ThreadPoolTest
的線程池,實現與其餘命名的線程池自然隔離,若是不配置andThreadPoolKey則使用withGroupKey配置來命名線程池
信號量隔離:線程隔離會帶來線程開銷,有些場景(好比無網絡請求場景)可能會由於用開銷換隔離得不償失,爲此hystrix提供了信號量隔離,當服務的併發數大於信號量閾值時將進入fallback。以demo爲例,經過withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
配置爲信號量隔離,經過withExecutionIsolationSemaphoreMaxConcurrentRequests
配置執行併發數不能大於3,因爲信號量隔離下不管調用哪一種命令執行方法,hystrix都不會建立新線程執行run()/construct()
,因此調用程序須要本身建立多個線程來模擬併發調用execute()
,最後看到一旦併發線程>3,後續請求都進入fallback
熔斷機制至關於電路的跳閘功能,舉個栗子,咱們能夠配置熔斷策略爲當請求錯誤比例在10s內>50%時,該服務將進入熔斷狀態,後續請求都會進入fallback。
以demo爲例,咱們經過withCircuitBreakerRequestVolumeThreshold
配置10s內請求數超過3個時熔斷器開始生效,經過withCircuitBreakerErrorThresholdPercentage
配置錯誤比例>80%時開始熔斷,而後for循環執行execute()
觸發run()
,在run()
裏,若是name
是小於10的偶數則正常返回,不然超時,經過屢次循環後,超時請求佔全部請求的比例將大於80%,就會看到後續請求都不進入run()
而是進入getFallback()
,由於再也不打印"running run():" + name
了。
除此以外,hystrix還支持多長時間從熔斷狀態自動恢復等功能,見下文附錄。
hystrix支持將一個請求結果緩存起來,下一個具備相同key的請求將直接從緩存中取出結果,減小請求開銷。要使用hystrix cache功能,第一個要求是重寫getCacheKey()
,用來構造cache key;第二個要求是構建context,若是請求B要用到請求A的結果緩存,A和B必須同處一個context。經過HystrixRequestContext.initializeContext()
和context.shutdown()
能夠構建一個context,這兩條語句間的全部請求都處於同一個context。
以demo的testWithCacheHits()
爲例,command2a、command2b、command2c同處一個context,前二者的cache key都是2HLX
(見getCacheKey()
),因此command2a執行完後把結果緩存,command2b執行時就不走run()
而是直接從緩存中取結果了,而command2c的cache key是2HLX1
,沒法從緩存中取結果。此外,經過isResponseFromCache()
可檢查返回結果是否來自緩存。
hystrix支持N個請求自動合併爲一個請求,這個功能在有網絡交互的場景下尤爲有用,好比每一個請求都要網絡訪問遠程資源,若是把請求合併爲一個,將使屢次網絡交互變成一次,極大節省開銷。重要一點,兩個請求能自動合併的前提是二者足夠「近」,即二者啓動執行的間隔時長要足夠小,默認爲10ms,即超過10ms將不自動合併。
以demo爲例,咱們連續發起多個queue請求,依次返回f1~f6共6個Future對象,根據打印結果可知f1~f5同處一個線程,說明這5個請求被合併了,而f6由另外一個線程執行,這是由於f5和f6中間隔了一個sleep,超過了合併要求的最大間隔時長。
根據http://hot66hot.iteye.com/blog/2155036 整理而得。
/* --------------統計相關------------------*/ // 統計滾動的時間窗口,默認:5000毫秒(取自circuitBreakerSleepWindowInMilliseconds) private final HystrixProperty metricsRollingStatisticalWindowInMilliseconds; // 統計窗口的Buckets的數量,默認:10個,每秒一個Buckets統計 private final HystrixProperty metricsRollingStatisticalWindowBuckets; // number of buckets in the statisticalWindow // 是否開啓監控統計功能,默認:true private final HystrixProperty metricsRollingPercentileEnabled; /* --------------熔斷器相關------------------*/ // 熔斷器在整個統計時間內是否開啓的閥值,默認20。也就是在metricsRollingStatisticalWindowInMilliseconds(默認10s)內至少請求20次,熔斷器才發揮起做用 private final HystrixProperty circuitBreakerRequestVolumeThreshold; // 熔斷時間窗口,默認:5秒.熔斷器中斷請求5秒後會進入半打開狀態,放下一個請求進來重試,若是該請求成功就關閉熔斷器,不然繼續等待一個熔斷時間窗口 private final HystrixProperty circuitBreakerSleepWindowInMilliseconds; //是否啓用熔斷器,默認true. 啓動 private final HystrixProperty circuitBreakerEnabled; //默認:50%。當出錯率超過50%後熔斷器啓動 private final HystrixProperty circuitBreakerErrorThresholdPercentage; //是否強制開啓熔斷器阻斷全部請求,默認:false,不開啓。置爲true時,全部請求都將被拒絕,直接到fallback private final HystrixProperty circuitBreakerForceOpen; //是否容許熔斷器忽略錯誤,默認false, 不開啓 private final HystrixProperty circuitBreakerForceClosed; /* --------------信號量相關------------------*/ //使用信號量隔離時,命令調用最大的併發數,默認:10 private final HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests; //使用信號量隔離時,命令fallback(降級)調用最大的併發數,默認:10 private final HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests; /* --------------其餘------------------*/ //使用命令調用隔離方式,默認:採用線程隔離,ExecutionIsolationStrategy.THREAD private final HystrixProperty executionIsolationStrategy; //使用線程隔離時,調用超時時間,默認:1秒 private final HystrixProperty executionIsolationThreadTimeoutInMilliseconds; //線程池的key,用於決定命令在哪一個線程池執行 private final HystrixProperty executionIsolationThreadPoolKeyOverride; //是否開啓fallback降級策略 默認:true private final HystrixProperty fallbackEnabled; // 使用線程隔離時,是否對命令執行超時的線程調用中斷(Thread.interrupt())操做.默認:true private final HystrixProperty executionIsolationThreadInterruptOnTimeout; // 是否開啓請求日誌,默認:true private final HystrixProperty requestLogEnabled; //是否開啓請求緩存,默認:true private final HystrixProperty requestCacheEnabled; // Whether request caching is enabled.
//請求合併是容許的最大請求數,默認: Integer.MAX_VALUE private final HystrixProperty maxRequestsInBatch; //批處理過程當中每一個命令延遲的時間,默認:10毫秒 private final HystrixProperty timerDelayInMilliseconds; //批處理過程當中是否開啓請求緩存,默認:開啓 private final HystrixProperty requestCacheEnabled;
/* 配置線程池大小,默認值10個. 建議值:請求高峯時99.5%的平均響應時間 + 向上預留一些便可 */ private final HystrixProperty corePoolSize; /* 配置線程值等待隊列長度,默認值:-1 建議值:-1表示不等待直接拒絕,測試代表線程池使用直接決絕策略+ 合適大小的非回縮線程池效率最高.因此不建議修改此值。 當使用非回縮線程池時,queueSizeRejectionThreshold,keepAliveTimeMinutes 參數無效 */ private final HystrixProperty maxQueueSize;
https://github.com/Netflix/Hystrix https://github.com/Netflix/Hystrix/wiki/How-To-Use http://hot66hot.iteye.com/blog/2155036