Hystrix使用入門(mén)手冊(cè)(中文)

導(dǎo)語(yǔ):網(wǎng)上資料(尤其中文文檔)對(duì)hystrix基礎(chǔ)功能的解釋比較籠統(tǒng),看了往往一頭霧水。為此,本文將通過(guò)若干demo,加入對(duì)官網(wǎng)How-it-Works的理解和翻譯,力求更清晰解釋hystrix的基礎(chǔ)功能。所用demo均對(duì)官網(wǎng)How-To-Use進(jìn)行了二次修改,見(jiàn)https://github.com/star2478/java-hystrix

Hystrix是Netflix開(kāi)源的一款容錯(cuò)系統(tǒng),能幫助使用者碼出具備強(qiáng)大的容錯(cuò)能力和魯棒性的程序。如果某程序或class要使用Hystrix,只需簡(jiǎn)單繼承HystrixCommand/HystrixObservableCommand并重寫(xiě)run()/construct(),然后調(diào)用程序?qū)嵗薱lass并執(zhí)行execute()/queue()/observe()/toObservable()。

// HelloWorldHystrixCommand要使用Hystrix功能 
public class HelloWorldHystrixCommand extends HystrixCommand {  
    private final String name; 
    public HelloWorldHystrixCommand(String name) {   
        super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));     
        this.name = name; 
    } 
    // 如果繼承的是HystrixObservableCommand,要重寫(xiě)Observable construct() 
    @Override 
    protected String run() {     
        return "Hello " + name; 
    } 
} 
/* 調(diào)用程序?qū)elloWorldHystrixCommand實(shí)例化,執(zhí)行execute()即觸發(fā)HelloWorldHystrixCommand.run()的執(zhí)行 */ 
String result = new HelloWorldHystrixCommand("HLX").execute();
System.out.println(result);  // 打印出Hello HLX 

pom.xml加上以下依賴(lài)。spring cloud也集成了hystrix,不過(guò)本文只介紹原生hystrix。

<dependency>
  <groupId>com.netflix.hystrix</groupId>
  <artifactId>hystrix-core</artifactId>
  <version>1.5.8</version>
</dependency>

本文重點(diǎn)介紹的是Hystrix各項(xiàng)基礎(chǔ)能力的用法及其效果,不從零介紹hystrix,要了解基礎(chǔ)知識(shí)推薦官網(wǎng)wiki民間blog

1、HystrixCommand vs HystrixObservableCommand

要想使用hystrix,只需要繼承HystrixCommandHystrixObservableCommand,簡(jiǎn)單用法見(jiàn)上面例子。兩者主要區(qū)別是:

  • 前者的命令邏輯寫(xiě)在run();后者的命令邏輯寫(xiě)在construct()

  • 前者的run()是由新創(chuàng)建的線(xiàn)程執(zhí)行;后者的construct()是由調(diào)用程序線(xiàn)程執(zhí)行

  • 前者一個(gè)實(shí)例只能向調(diào)用程序發(fā)送(emit)單條數(shù)據(jù),比如上面例子中run()只能返回一個(gè)String結(jié)果;后者一個(gè)實(shí)例可以順序發(fā)送多條數(shù)據(jù),比如demo中順序調(diào)用多個(gè)onNext(),便實(shí)現(xiàn)了向調(diào)用程序發(fā)送多條數(shù)據(jù),甚至還能發(fā)送一個(gè)范圍的數(shù)據(jù)集

2、4個(gè)命令執(zhí)行方法

execute()、queue()observe()、toObservable()這4個(gè)方法用來(lái)觸發(fā)執(zhí)行run()/construct(),一個(gè)實(shí)例只能執(zhí)行一次這4個(gè)方法,特別說(shuō)明的是HystrixObservableCommand沒(méi)有execute()queue()。

4個(gè)方法的主要區(qū)別是:

  • execute():以同步堵塞方式執(zhí)行run()。以demo為例,調(diào)用execute()后,hystrix先創(chuàng)建一個(gè)新線(xiàn)程運(yùn)行run(),接著調(diào)用程序要在execute()調(diào)用處一直堵塞著,直到run()運(yùn)行完成

  • queue():以異步非堵塞方式執(zhí)行run()。以demo為例,一調(diào)用queue()就直接返回一個(gè)Future對(duì)象,同時(shí)hystrix創(chuàng)建一個(gè)新線(xiàn)程運(yùn)行run(),調(diào)用程序通過(guò)Future.get()拿到run()的返回結(jié)果,而Future.get()是堵塞執(zhí)行的

  • observe():事件注冊(cè)前執(zhí)行run()/construct()。以demo為例,第一步是事件注冊(cè)前,先調(diào)用observe()自動(dòng)觸發(fā)執(zhí)行run()/construct()(如果繼承的是HystrixCommand,hystrix將創(chuàng)建新線(xiàn)程非堵塞執(zhí)行run();如果繼承的是HystrixObservableCommand,將以調(diào)用程序線(xiàn)程堵塞執(zhí)行construct()),第二步是從observe()返回后調(diào)用程序調(diào)用subscribe()完成事件注冊(cè),如果run()/construct()執(zhí)行成功則觸發(fā)onNext()onCompleted(),如果執(zhí)行異常則觸發(fā)onError()

  • toObservable():事件注冊(cè)后執(zhí)行run()/construct()。以demo為例,第一步是事件注冊(cè)前,一調(diào)用toObservable()就直接返回一個(gè)Observable<String>對(duì)象,第二步調(diào)用subscribe()完成事件注冊(cè)后自動(dòng)觸發(fā)執(zhí)行run()/construct()(如果繼承的是HystrixCommand,hystrix將創(chuàng)建新線(xiàn)程非堵塞執(zhí)行run(),調(diào)用程序不必等待run();如果繼承的是HystrixObservableCommand,將以調(diào)用程序線(xiàn)程堵塞執(zhí)行construct(),調(diào)用程序等待construct()執(zhí)行完才能繼續(xù)往下走),如果run()/construct()執(zhí)行成功則觸發(fā)onNext()onCompleted(),如果執(zhí)行異常則觸發(fā)onError()

3、fallback(降級(jí))

使用fallback機(jī)制很簡(jiǎn)單,繼承HystrixCommand只需重寫(xiě)getFallback(),繼承HystrixObservableCommand只需重寫(xiě)resumeWithFallback(),比如HelloWorldHystrixCommand加上下面代碼片段:

@Override
protected String getFallback() {
    return "fallback: " + name;
}

fallback實(shí)際流程是當(dāng)run()/construct()被觸發(fā)執(zhí)行時(shí)或執(zhí)行中發(fā)生錯(cuò)誤時(shí),將轉(zhuǎn)向執(zhí)行getFallback()/resumeWithFallback()。結(jié)合下圖,4種錯(cuò)誤情況將觸發(fā)fallback:

  • 非HystrixBadRequestException異常:以demo為例,當(dāng)拋出HystrixBadRequestException時(shí),調(diào)用程序可以捕獲異常,沒(méi)有觸發(fā)getFallback(),而其他異常則會(huì)觸發(fā)getFallback(),調(diào)用程序?qū)@得getFallback()的返回

  • run()/construct()運(yùn)行超時(shí):以demo為例,使用無(wú)限while循環(huán)或sleep模擬超時(shí),觸發(fā)了getFallback()

  • 熔斷器啟動(dòng):以demo為例,我們配置10s內(nèi)請(qǐng)求數(shù)大于3個(gè)時(shí)就啟動(dòng)熔斷器,請(qǐng)求錯(cuò)誤率大于80%時(shí)就熔斷,然后for循環(huán)發(fā)起請(qǐng)求,當(dāng)請(qǐng)求符合熔斷條件時(shí)將觸發(fā)getFallback()。更多熔斷策略見(jiàn)下文

  • 線(xiàn)程池/信號(hào)量已滿(mǎn):以demo為例,我們配置線(xiàn)程池?cái)?shù)目為3,然后先用一個(gè)for循環(huán)執(zhí)行queue(),觸發(fā)的run()sleep 2s,然后再用第2個(gè)for循環(huán)執(zhí)行execute(),發(fā)現(xiàn)所有execute()都觸發(fā)了fallback,這是因?yàn)榈?個(gè)for的線(xiàn)程還在sleep,占用著線(xiàn)程池所有線(xiàn)程,導(dǎo)致第2個(gè)for的所有命令都無(wú)法獲取到線(xiàn)程

來(lái)自hystrix github wiki

調(diào)用程序可以通過(guò)isResponseFromFallback()查詢(xún)結(jié)果是由run()/construct()還是getFallback()/resumeWithFallback()返回的

4、隔離策略

hystrix提供了兩種隔離策略:線(xiàn)程池隔離和信號(hào)量隔離。hystrix默認(rèn)采用線(xiàn)程池隔離。

  • 線(xiàn)程池隔離:不同服務(wù)通過(guò)使用不同線(xiàn)程池,彼此間將不受影響,達(dá)到隔離效果。以demo為例,我們通過(guò)andThreadPoolKey配置使用命名為ThreadPoolTest的線(xiàn)程池,實(shí)現(xiàn)與其他命名的線(xiàn)程池天然隔離,如果不配置andThreadPoolKey則使用withGroupKey配置來(lái)命名線(xiàn)程池

  • 信號(hào)量隔離:線(xiàn)程隔離會(huì)帶來(lái)線(xiàn)程開(kāi)銷(xiāo),有些場(chǎng)景(比如無(wú)網(wǎng)絡(luò)請(qǐng)求場(chǎng)景)可能會(huì)因?yàn)橛瞄_(kāi)銷(xiāo)換隔離得不償失,為此hystrix提供了信號(hào)量隔離,當(dāng)服務(wù)的并發(fā)數(shù)大于信號(hào)量閾值時(shí)將進(jìn)入fallback。以demo為例,通過(guò)withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)配置為信號(hào)量隔離,通過(guò)withExecutionIsolationSemaphoreMaxConcurrentRequests配置執(zhí)行并發(fā)數(shù)不能大于3,由于信號(hào)量隔離下無(wú)論調(diào)用哪種命令執(zhí)行方法,hystrix都不會(huì)創(chuàng)建新線(xiàn)程執(zhí)行run()/construct(),所以調(diào)用程序需要自己創(chuàng)建多個(gè)線(xiàn)程來(lái)模擬并發(fā)調(diào)用execute(),最后看到一旦并發(fā)線(xiàn)程>3,后續(xù)請(qǐng)求都進(jìn)入fallback

5、熔斷機(jī)制

熔斷機(jī)制相當(dāng)于電路的跳閘功能,舉個(gè)栗子,我們可以配置熔斷策略為當(dāng)請(qǐng)求錯(cuò)誤比例在10s內(nèi)>50%時(shí),該服務(wù)將進(jìn)入熔斷狀態(tài),后續(xù)請(qǐng)求都會(huì)進(jìn)入fallback。

demo為例,我們通過(guò)withCircuitBreakerRequestVolumeThreshold配置10s內(nèi)請(qǐng)求數(shù)超過(guò)3個(gè)時(shí)熔斷器開(kāi)始生效,通過(guò)withCircuitBreakerErrorThresholdPercentage配置錯(cuò)誤比例>80%時(shí)開(kāi)始熔斷,然后for循環(huán)執(zhí)行execute()觸發(fā)run(),在run()里,如果name是小于10的偶數(shù)則正常返回,否則超時(shí),通過(guò)多次循環(huán)后,超時(shí)請(qǐng)求占所有請(qǐng)求的比例將大于80%,就會(huì)看到后續(xù)請(qǐng)求都不進(jìn)入run()而是進(jìn)入getFallback(),因?yàn)椴辉俅蛴?code>"running run():" + name了。

除此之外,hystrix還支持多長(zhǎng)時(shí)間從熔斷狀態(tài)自動(dòng)恢復(fù)等功能,見(jiàn)下文附錄。

6、結(jié)果cache

hystrix支持將一個(gè)請(qǐng)求結(jié)果緩存起來(lái),下一個(gè)具有相同key的請(qǐng)求將直接從緩存中取出結(jié)果,減少請(qǐng)求開(kāi)銷(xiāo)。要使用hystrix cache功能,第一個(gè)要求是重寫(xiě)getCacheKey(),用來(lái)構(gòu)造cache key;第二個(gè)要求是構(gòu)建context,如果請(qǐng)求B要用到請(qǐng)求A的結(jié)果緩存,A和B必須同處一個(gè)context。通過(guò)HystrixRequestContext.initializeContext()context.shutdown()可以構(gòu)建一個(gè)context,這兩條語(yǔ)句間的所有請(qǐng)求都處于同一個(gè)context。

demotestWithCacheHits()為例,command2a、command2b、command2c同處一個(gè)context,前兩者的cache key都是2HLX(見(jiàn)getCacheKey()),所以command2a執(zhí)行完后把結(jié)果緩存,command2b執(zhí)行時(shí)就不走run()而是直接從緩存中取結(jié)果了,而command2c的cache key是2HLX1,無(wú)法從緩存中取結(jié)果。此外,通過(guò)isResponseFromCache()可檢查返回結(jié)果是否來(lái)自緩存。

7、合并請(qǐng)求collapsing

hystrix支持N個(gè)請(qǐng)求自動(dòng)合并為一個(gè)請(qǐng)求,這個(gè)功能在有網(wǎng)絡(luò)交互的場(chǎng)景下尤其有用,比如每個(gè)請(qǐng)求都要網(wǎng)絡(luò)訪(fǎng)問(wèn)遠(yuǎn)程資源,如果把請(qǐng)求合并為一個(gè),將使多次網(wǎng)絡(luò)交互變成一次,極大節(jié)省開(kāi)銷(xiāo)。重要一點(diǎn),兩個(gè)請(qǐng)求能自動(dòng)合并的前提是兩者足夠“近”,即兩者啟動(dòng)執(zhí)行的間隔時(shí)長(zhǎng)要足夠小,默認(rèn)為10ms,即超過(guò)10ms將不自動(dòng)合并。

demo為例,我們連續(xù)發(fā)起多個(gè)queue請(qǐng)求,依次返回f1~f6共6個(gè)Future對(duì)象,根據(jù)打印結(jié)果可知f1~f5同處一個(gè)線(xiàn)程,說(shuō)明這5個(gè)請(qǐng)求被合并了,而f6由另一個(gè)線(xiàn)程執(zhí)行,這是因?yàn)?em>f5和f6中間隔了一個(gè)sleep,超過(guò)了合并要求的最大間隔時(shí)長(zhǎng)。

附錄:各種策略配置

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

  • HystrixCommandProperties
/* --------------統(tǒng)計(jì)相關(guān)------------------*/ 
// 統(tǒng)計(jì)滾動(dòng)的時(shí)間窗口,默認(rèn):5000毫秒(取自circuitBreakerSleepWindowInMilliseconds)   
private final HystrixProperty metricsRollingStatisticalWindowInMilliseconds;   
// 統(tǒng)計(jì)窗口的Buckets的數(shù)量,默認(rèn):10個(gè),每秒一個(gè)Buckets統(tǒng)計(jì)   
private final HystrixProperty metricsRollingStatisticalWindowBuckets; // number of buckets in the statisticalWindow   
// 是否開(kāi)啟監(jiān)控統(tǒng)計(jì)功能,默認(rèn):true   
private final HystrixProperty metricsRollingPercentileEnabled;   
/* --------------熔斷器相關(guān)------------------*/ 
// 熔斷器在整個(gè)統(tǒng)計(jì)時(shí)間內(nèi)是否開(kāi)啟的閥值,默認(rèn)20。也就是在metricsRollingStatisticalWindowInMilliseconds(默認(rèn)10s)內(nèi)至少請(qǐng)求20次,熔斷器才發(fā)揮起作用   
private final HystrixProperty circuitBreakerRequestVolumeThreshold;   
// 熔斷時(shí)間窗口,默認(rèn):5秒.熔斷器中斷請(qǐng)求5秒后會(huì)進(jìn)入半打開(kāi)狀態(tài),放下一個(gè)請(qǐng)求進(jìn)來(lái)重試,如果該請(qǐng)求成功就關(guān)閉熔斷器,否則繼續(xù)等待一個(gè)熔斷時(shí)間窗口
private final HystrixProperty circuitBreakerSleepWindowInMilliseconds;   
//是否啟用熔斷器,默認(rèn)true. 啟動(dòng)   
private final HystrixProperty circuitBreakerEnabled;   
//默認(rèn):50%。當(dāng)出錯(cuò)率超過(guò)50%后熔斷器啟動(dòng)
private final HystrixProperty circuitBreakerErrorThresholdPercentage;  
//是否強(qiáng)制開(kāi)啟熔斷器阻斷所有請(qǐng)求,默認(rèn):false,不開(kāi)啟。置為true時(shí),所有請(qǐng)求都將被拒絕,直接到fallback 
private final HystrixProperty circuitBreakerForceOpen;   
//是否允許熔斷器忽略錯(cuò)誤,默認(rèn)false, 不開(kāi)啟   
private final HystrixProperty circuitBreakerForceClosed; 
/* --------------信號(hào)量相關(guān)------------------*/ 
//使用信號(hào)量隔離時(shí),命令調(diào)用最大的并發(fā)數(shù),默認(rèn):10   
private final HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests;   
//使用信號(hào)量隔離時(shí),命令fallback(降級(jí))調(diào)用最大的并發(fā)數(shù),默認(rèn):10   
private final HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests; 
/* --------------其他------------------*/ 
//使用命令調(diào)用隔離方式,默認(rèn):采用線(xiàn)程隔離,ExecutionIsolationStrategy.THREAD   
private final HystrixProperty executionIsolationStrategy;   
//使用線(xiàn)程隔離時(shí),調(diào)用超時(shí)時(shí)間,默認(rèn):1秒   
private final HystrixProperty executionIsolationThreadTimeoutInMilliseconds;   
//線(xiàn)程池的key,用于決定命令在哪個(gè)線(xiàn)程池執(zhí)行   
private final HystrixProperty executionIsolationThreadPoolKeyOverride;   
//是否開(kāi)啟fallback降級(jí)策略 默認(rèn):true   
private final HystrixProperty fallbackEnabled;   
// 使用線(xiàn)程隔離時(shí),是否對(duì)命令執(zhí)行超時(shí)的線(xiàn)程調(diào)用中斷(Thread.interrupt())操作.默認(rèn):true   
private final HystrixProperty executionIsolationThreadInterruptOnTimeout; 
// 是否開(kāi)啟請(qǐng)求日志,默認(rèn):true   
private final HystrixProperty requestLogEnabled;   
//是否開(kāi)啟請(qǐng)求緩存,默認(rèn):true   
private final HystrixProperty requestCacheEnabled; // Whether request caching is enabled. 
  • HystrixCollapserProperties
//請(qǐng)求合并是允許的最大請(qǐng)求數(shù),默認(rèn): Integer.MAX_VALUE   
private final HystrixProperty maxRequestsInBatch;   
//批處理過(guò)程中每個(gè)命令延遲的時(shí)間,默認(rèn):10毫秒   
private final HystrixProperty timerDelayInMilliseconds;   
//批處理過(guò)程中是否開(kāi)啟請(qǐng)求緩存,默認(rèn):開(kāi)啟   
private final HystrixProperty requestCacheEnabled; 
  • HystrixThreadPoolProperties
/* 配置線(xiàn)程池大小,默認(rèn)值10個(gè) */ 
private final HystrixProperty corePoolSize; 
/* 配置線(xiàn)程值等待隊(duì)列長(zhǎng)度,默認(rèn)值:-1 建議值:-1表示不等待直接拒絕,測(cè)試表明線(xiàn)程池使用直接決絕策略+ 合適大小的非回縮線(xiàn)程池效率最高.所以不建議修改此值。 當(dāng)使用非回縮線(xiàn)程池時(shí),queueSizeRejectionThreshold,keepAliveTimeMinutes 參數(shù)無(wú)效 */
private final HystrixProperty maxQueueSize; 

參考文獻(xiàn)

https://github.com/Netflix/Hystrix
https://github.com/Netflix/Hystrix/wiki/How-To-Use
http://hot66hot.iteye.com/blog/2155036

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評(píng)論 19 139
  • 一、認(rèn)識(shí)Hystrix Hystrix是Netflix開(kāi)源的一款容錯(cuò)框架,包含常用的容錯(cuò)方法:線(xiàn)程池隔離、信號(hào)量隔...
    新棟BOOK閱讀 26,724評(píng)論 1 37
  • 一、認(rèn)識(shí)Hystrix Hystrix是Netflix開(kāi)源的一款容錯(cuò)框架,包含常用的容錯(cuò)方法:線(xiàn)程池隔離、信號(hào)量隔...
    新棟BOOK閱讀 4,245評(píng)論 0 19
  • (git上的源碼:https://gitee.com/rain7564/spring_microservices_...
    sprainkle閱讀 9,539評(píng)論 13 33
  • 本篇會(huì)介紹下Hystrix的使用,會(huì)從最簡(jiǎn)單的入門(mén)實(shí)例開(kāi)始,然后會(huì)講述下Hystrix的隔離和熔斷的原理和流程,最...
    monkey01閱讀 6,385評(píng)論 0 6

友情鏈接更多精彩內(nèi)容