Hystrix是什么
分布式服務系統(tǒng)通常會通過HTTP或RPC方式調用所依賴的服務宽菜,例如支付服務通過HTTP或RPC調用銀行卡服務恶耽。在高并發(fā)請求的情景下,依賴的服務可能會出現(xiàn)服務異常挥唠、網(wǎng)絡連接緩慢医吊、資源繁忙、暫時不可用贾陷、服務脫機等情況缘眶,這些異常情況將會嚴重影響整個線上系統(tǒng)的穩(wěn)定性和可用性,最糟糕的情況是產(chǎn)生服務雪崩效應昵宇。復雜的分布式服務系統(tǒng)往往會依賴更多的其它服務磅崭,在高并發(fā)的情況下,如果沒有做好隔離措施瓦哎,這些依賴將會拖垮整個服務調用者砸喻。Hystrix是Netflix的一個幫助解決分布式服務系統(tǒng)交互時超時處理和容錯的類庫,它具有降級和熔斷的保護能力蒋譬,可以優(yōu)雅的解決上述問題割岛。
Hystrix能做什么
Hystrix提供了如下功能特性:
1.Hystrix把服務調用統(tǒng)稱為依賴調用,Hystrix通過使用命令模式將依賴調用邏輯封裝在HystrixCommand中犯助,每一次的依賴調用將在Hystrix的單獨線程池(或信號)中執(zhí)行癣漆;
2.可根據(jù)業(yè)務需要配置依賴分組名、線程池剂买,使不同分組的依賴可以在不同的線程池中執(zhí)行惠爽,隔離不同依賴調用的資源;
3.可配置依賴調用超時時間(一般配置為比99.5%平均調用時間略高)瞬哼,當依賴調用超時時直接返回或執(zhí)行getFallback方法婚肆;
4.依賴調用異常、超時坐慰、短路時將執(zhí)行getFallback方法较性;
5.提供了熔斷器(CircuitBreaker)機制,可根據(jù)設定的條件(如調用失敗率大于50%)判斷依賴調用是否可以繼續(xù)被調用,如果某個依賴調用的錯誤百分比超過閾值赞咙,則通過手動或自動地中斷一個熔斷器责循,一段時間內依賴調用無法被執(zhí)行;
6.提供了對依賴調用的實時統(tǒng)計和監(jiān)控攀操。
Hystrix如何使用
1.添加Hystrix依賴
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
2.使用HystrixCommand封裝依賴調用
通過繼承HystrixCommand封裝依賴調用院仿,示例代碼如下:
public class CommandHelloWorld extends HystrixCommand<String> {
private final String name;
public CommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"));
this.name = name;
}
@Override
protected String run() throws Exception {
// 在這里調用依賴
Thread.sleep(500L);
return "Hello " + name + "--" + Thread.currentThread().getId();
}
// 超時建邓、異常后執(zhí)行該方法
@Override
protected String getFallback() {
return "fallback";
}
}
run方法:run方法體中進行依賴調用
getFallback方法:當run方法中依賴調用超時、異常(除了HystrixBadRequestException)時會執(zhí)行getFallback方法快速返回分别;當run方法中的依賴調用在設置的時間內超時称簿、異常(除了HystrixBadRequestException)的頻率超過閾值,后續(xù)對這個依賴的調用將直接執(zhí)行getFallback方法摸柄,待冷卻一段時間后,對這個依賴的調用會重新進入run方法執(zhí)行。
3.執(zhí)行封裝的依賴調用
3.1同步執(zhí)行
調用execute方法即為同步執(zhí)行慈迈,當前線程將一直阻塞,直到獲取結果省有,示例代碼如下:
@Test
public void testSynchronous() {
CommandHelloWorld commandHelloWorld = new CommandHelloWorld("jack");
System.out.print(commandHelloWorld.execute() + "--" + Thread.currentThread().getId());
}
輸出結果如下:
Hello jack--16--1
從輸出結果可以看到依賴調用線程和主線程不是同一個痒留,實現(xiàn)了線程隔離。
HystrixCommand默認的調用超時時間是1000毫秒蠢沿,如果將上述run方法中的線程休眠時間改成1100毫秒伸头,再次運行testSynchronous單元測試,將得到如下結果:
fallback--1
可以看到在依賴調用時間超過設置的默認超時時間時舷蟀,將執(zhí)行getFallback方法快速返回恤磷,實現(xiàn)優(yōu)雅降級,其過程如下圖所示野宜。
3.2異步執(zhí)行
調用queue方法即為異步執(zhí)行扫步,不阻塞當前線程,返回一個Future對象匈子,示例代碼如下:
@Test
public void testAsynchronous() throws Exception {
CommandHelloWorld commandHelloWorld = new CommandHelloWorld("jack");
Future<String> future = commandHelloWorld.queue();
System.out.println(future.get() + "--" + Thread.currentThread().getId());
}
輸出結果如下:
Hello jack--16--1
queue().get()等同于同步調用execute()
3.3熱注冊觀察者執(zhí)行
調用observe方法即為熱注冊觀察者執(zhí)行河胎,返回一個Observable對象,當run方法執(zhí)行完成后虎敦,進入觀察者訂閱的事件中游岳,示例代碼如下:
@Test
public void testHotObservable() throws Exception {
CommandHelloWorld commandHelloWorld = new CommandHelloWorld("jack");
Observable<String> ho = commandHelloWorld.observe();
//訂閱結果回調事件
ho.subscribe(new Action1<String>() {
public void call(String result) {
//result為run方法執(zhí)行返回的結果
System.out.println(result + "--" + Thread.currentThread().getId());
}
});
Thread.sleep(1000);
//訂閱一個完整的回調事件
ho.subscribe(new Subscriber<String>() {
//在onNext執(zhí)行后執(zhí)行
public void onCompleted() {
System.out.println("oonCompleted ");
}
//在run/onNext方法執(zhí)行異常后執(zhí)行
public void onError(Throwable throwable) {
}
//在run方法返回結果后執(zhí)行
public void onNext(String s) {
System.out.println("onNext: " + s );
}
});
}
輸出結果如下:
Hello jack--16--16
onNext: Hello jack--16
oonCompleted
3.4冷注冊觀察者執(zhí)行
調用toObservable方法即為冷注冊觀察者執(zhí)行,同樣返回Observable對象其徙,但它是在注冊的時即執(zhí)行run方法胚迫,示例代碼如下:
@Test
public void testColdObservable() throws Exception {
CommandHelloWorld commandHelloWorld = new CommandHelloWorld("jack");
Observable<String> ho = commandHelloWorld.toObservable();
ho.subscribe(new Action1<String>() {
public void call(String s) {
System.out.println(s + "--" + Thread.currentThread().getId());
}
});
Thread.sleep(1000);
}
輸出結果如下:
Hello jack--16--16
前面三種調用方式,最終都是依賴toObservable方式擂橘,這其中的轉換如下圖所示:
屬性配置
查看HystrixCommand源碼晌区,可以發(fā)現(xiàn)一個常用的構造方法HystrixCommand(HystrixCommand.Setter setter),使用方法如下:
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("HelloWorld");
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("hello");
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("hello");
HystrixCommand.Setter setter = HystrixCommand.Setter
.withGroupKey(groupKey)
.andCommandKey(commandKey)
.andThreadPoolKey(threadPoolKey);
HystrixCommand<String> helloCommand = new HystrixCommand<String>(setter) {
protected String run() throws Exception {
//依賴調用
return "run";
}
@Override
protected String getFallback() {
//fail back
return super.getFallback();
}
};
這個HystrixCommand.Setter中包含了如下屬性:
protected final HystrixCommandGroupKey groupKey;
protected HystrixCommandKey commandKey;
protected HystrixThreadPoolKey threadPoolKey;
protected com.netflix.hystrix.HystrixCommandProperties.Setter commandPropertiesDefaults;
protected com.netflix.hystrix.HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults;
1 HystrixCommandKey
Hystrix使用單例模式存儲HystrixCommand,熔斷機制就是根據(jù)單實例上的調用情況統(tǒng)計實現(xiàn)的朗若,所以每個HystrixCommand要有自己的名字恼五,用于區(qū)分,同時用于依賴調用的隔離哭懈。HystrixCommandKey就是用于定義這個名字灾馒,如果沒有定義這個名字,Hystrix會使用其類名作為其名字遣总,可以使用HystrixCommandKey.Factory.asKey(String name)方法定義一個名稱睬罗。
2 HystrixThreadPoolKey
HystrixThreadPoolKey是HystrixCommand所在的線程池,如果該參數(shù)不設置則使用HystrixCommandGroupKey作為HystrixThreadPoolKey旭斥,這種情況下同一個HystrixCommandGroupKey下的依賴調用共用同一個線程池內容达,如果不想共用同一個線程池,則需要設置該參數(shù)垂券』ㄑ危可以使用HystrixThreadPoolKey.Factory.asKey(String name)方法設置。
3 HystrixCommandGroupKey
Hystrix需要對HystrixCommand進行分組菇爪,便于統(tǒng)計算芯、管理,所以需要一個分組名稱凳宙,HystrixCommandGroupKey就是用于定義分組名稱熙揍,可以使用HystrixCommandGroupKey.Factory.asKey(String name)方法定義一個分組名。每個HystrixCommand必須要配置一個分組名氏涩,一個是用于分組届囚,還有如果沒有配置HystrixThreadPoolKey,這個分組名將會用于線程池名削葱。
4 HystrixThreadPoolProperties
從名稱上可以看出這是線程池的屬性配置奖亚,可以通過它設置核心線程數(shù)大小、最大線程數(shù)析砸、任務隊列大小等昔字,當然它也又一些默認的配置參數(shù)。
5 HystrixCommandProperties
這個就是HystrixCommand的屬性配置首繁,它可以設置熔斷器是否可用作郭、熔斷器熔斷的錯誤百分比、依賴調用超時時間等弦疮,它有一些默認的配置參數(shù)夹攒,如熔斷器熔斷的錯誤百分比默認值是50%、依賴調用超時時間默認值是1000毫秒胁塞。
隔離方式
Hystrix支持線程隔離和信號量隔離:
線程隔離
不同的依賴調用分配到不同的線程池中執(zhí)行咏尝,使用線程對依賴調用進行隔離压语,上述的示例代碼就是使用線程隔離。優(yōu)點是隔離性能好编检,可設置短路機制(依賴調用失敗后執(zhí)行getFallback()或依賴調用熔斷后胎食,一段時間內對該依賴的調用將直接返回失敗),缺點是涉及到線程切換的性能損耗允懂,但是官方給出的結果是性能損耗是可以接受的厕怜。
信號量隔離
信號量隔離可實現(xiàn)對依賴調用最高并發(fā)請求數(shù)的限制,每次依賴調用都會先判斷信號量是否達到閾值蕾总,如果達到極限值則拒絕調用粥航,優(yōu)點是不用新啟線程,缺點是每次都需要獲取信號量生百,使用方式如下:
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("HelloWorld");
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("hello");
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("hello");
//配置信號量隔離
HystrixCommandProperties.Setter commandPropertiesSetter = HystrixCommandProperties.Setter().withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE);
HystrixCommand.Setter setter = HystrixCommand.Setter
.withGroupKey(groupKey)
.andCommandKey(commandKey)
.andThreadPoolKey(threadPoolKey)
.andCommandPropertiesDefaults(commandPropertiesSetter);
Hystrix工作過程
工程過程如下圖所示:
步驟描述如下:
1.使用HystrixCommand或HystrixObservableCommand封裝一個依賴調用递雀;
2.執(zhí)行封裝的依賴調用;
3.判斷本次調用是否可以從緩存中取結果置侍,如果可以映之,直接返回緩存的結果;如果不可以進入第4步判斷蜡坊;
4.判斷熔斷器是否打開,如果打開則進入第8步赎败;如果沒打開則進入第5步秕衙;
5.判斷信號量或線程池是否已滿,如果已滿則進入第8步僵刮;如果沒滿則進入第6步据忘;
6.執(zhí)行依賴調用,調用失敗或超時進入第8步搞糕;調用成功返回結果勇吊;
7.根據(jù)依賴調用成功、失敗或超時計算熔斷值窍仰;
8.getFallback()執(zhí)行失敗或沒實現(xiàn)getFallback()方法汉规,將拋出異常;getFallback()執(zhí)行成功返回fallback值驹吮。
Hystrix熔斷保護機制
Hystrix熔斷保護就像電路中的熔斷器一樣针史,在電壓過高時,保險絲會熔斷碟狞,防止火災啄枕,做到用電安全。熔斷保護機制的工作過程如下圖所示:
熔斷器工作過程如下:
1.假設大量的請求數(shù)量超過了HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()的閾值族沃,并且依賴調用失敗的百分比超過了HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()的閾值频祝,熔斷器將會從關閉狀態(tài)變成打開狀態(tài)泌参;
2.在熔斷器處于打開狀態(tài)的期間,所有對這個依賴進行的調用都會短路常空,即不進行真正的依賴調用沽一,返回失敗窟绷;
3.在等待(冷卻)的時間超過HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()的值后锯玛,熔斷器將處于半開的狀態(tài),將允許單個請求去調用依賴兼蜈,如果這次的依賴調用還是失敗攘残,熔斷器狀態(tài)將再次變成打開,這個打開狀態(tài)持續(xù)時間是HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()配置的值为狸;如果這次的依賴調用成功歼郭,熔斷器狀態(tài)將變成關閉,后續(xù)依賴調用可正常執(zhí)行辐棒。
依賴調用監(jiān)控
Hystrix提供了Hystrix Dashboard功能病曾,可以實時監(jiān)控依賴的調用情況。