Hystrix使用

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

一、HystrixCommand vs HystrixObservableCommand

要想使用hystrix,只須要繼承HystrixCommandHystrixObservableCommand,簡單用法見上面例子。二者主要區別是:緩存

  • 前者的命令邏輯寫在run();後者的命令邏輯寫在construct()網絡

  • 前者的run()是由新建立的線程執行;後者的construct()是由調用程序線程執行併發

  • 前者一個實例只能向調用程序發送(emit)單條數據,好比上面例子中run()只能返回一個String結果;後者一個實例能夠順序發送多條數據,好比demo中順序調用多個onNext(),便實現了向調用程序發送多條數據,甚至還能發送一個範圍的數據集異步

二、4個命令執行方法

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(降級)

使用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的全部命令都沒法獲取到線程

 
來自hystrix github wiki

調用程序能夠經過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還支持多長時間從熔斷狀態自動恢復等功能,見下文附錄。

六、結果cache

hystrix支持將一個請求結果緩存起來,下一個具備相同key的請求將直接從緩存中取出結果,減小請求開銷。要使用hystrix cache功能,第一個要求是重寫getCacheKey(),用來構造cache key;第二個要求是構建context,若是請求B要用到請求A的結果緩存,A和B必須同處一個context。經過HystrixRequestContext.initializeContext()context.shutdown()能夠構建一個context,這兩條語句間的全部請求都處於同一個context。

以demo的testWithCacheHits()爲例,command2acommand2bcommand2c同處一個context,前二者的cache key都是2HLX(見getCacheKey()),因此command2a執行完後把結果緩存,command2b執行時就不走run()而是直接從緩存中取結果了,而command2c的cache key是2HLX1,沒法從緩存中取結果。此外,經過isResponseFromCache()可檢查返回結果是否來自緩存。

七、合併請求collapsing

hystrix支持N個請求自動合併爲一個請求,這個功能在有網絡交互的場景下尤爲有用,好比每一個請求都要網絡訪問遠程資源,若是把請求合併爲一個,將使屢次網絡交互變成一次,極大節省開銷。重要一點,兩個請求能自動合併的前提是二者足夠「近」,即二者啓動執行的間隔時長要足夠小,默認爲10ms,即超過10ms將不自動合併。

以demo爲例,咱們連續發起多個queue請求,依次返回f1~f6共6個Future對象,根據打印結果可知f1~f5同處一個線程,說明這5個請求被合併了,而f6由另外一個線程執行,這是由於f5f6中間隔了一個sleep,超過了合併要求的最大間隔時長。

附錄:各類策略配置

根據http://hot66hot.iteye.com/blog/2155036 整理而得。

  • HystrixCommandProperties
/* --------------統計相關------------------*/ 
// 統計滾動的時間窗口,默認: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.
  • HystrixCollapserProperties
//請求合併是容許的最大請求數,默認: Integer.MAX_VALUE   
private final HystrixProperty maxRequestsInBatch;   
//批處理過程當中每一個命令延遲的時間,默認:10毫秒   
private final HystrixProperty timerDelayInMilliseconds;   
//批處理過程當中是否開啓請求緩存,默認:開啓   
private final HystrixProperty requestCacheEnabled;
  • HystrixThreadPoolProperties
/* 配置線程池大小,默認值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

相關文章
相關標籤/搜索