Hystrix實現(xiàn)分布式系統(tǒng)中的故障容錯

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)雅降級,其過程如下圖所示野宜。

getFallback.png

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方式擂橘,這其中的轉換如下圖所示:

hystrix-return-flow.png

屬性配置

查看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工作過程

工程過程如下圖所示:

hystrix-command-flow-chart.png

步驟描述如下:
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熔斷保護就像電路中的熔斷器一樣针史,在電壓過高時,保險絲會熔斷碟狞,防止火災啄枕,做到用電安全。熔斷保護機制的工作過程如下圖所示:

circuit-breaker-1280.png

熔斷器工作過程如下:
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)控依賴的調用情況。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末漾根,一起剝皮案震驚了整個濱河市泰涂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辐怕,老刑警劉巖逼蒙,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異寄疏,居然都是意外死亡是牢,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門陕截,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驳棱,“玉大人,你說我怎么就攤上這事农曲∩缃粒” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵朋蔫,是天一觀的道長罚渐。 經(jīng)常有香客問我,道長驯妄,這世上最難降的妖魔是什么荷并? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮青扔,結果婚禮上源织,老公的妹妹穿的比我還像新娘翩伪。我一直安慰自己,他們只是感情好谈息,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布缘屹。 她就那樣靜靜地躺著,像睡著了一般侠仇。 火紅的嫁衣襯著肌膚如雪轻姿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天逻炊,我揣著相機與錄音互亮,去河邊找鬼。 笑死余素,一個胖子當著我的面吹牛豹休,可吹牛的內容都是我干的。 我是一名探鬼主播桨吊,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼威根,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了视乐?” 一聲冷哼從身側響起洛搀,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎佑淀,沒想到半個月后姥卢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡渣聚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了僧叉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奕枝。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瓶堕,靈堂內的尸體忽然破棺而出隘道,到底是詐尸還是另有隱情,我是刑警寧澤郎笆,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布谭梗,位于F島的核電站,受9級特大地震影響宛蚓,放射性物質發(fā)生泄漏激捏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一凄吏、第九天 我趴在偏房一處隱蔽的房頂上張望远舅。 院中可真熱鬧闰蛔,春花似錦、人聲如沸图柏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚤吹。三九已至例诀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間裁着,已是汗流浹背繁涂。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留跨算,地道東北人爆土。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像诸蚕,于是被迫代替她去往敵國和親步势。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內容

  • (git上的源碼:https://gitee.com/rain7564/spring_microservices_...
    sprainkle閱讀 9,339評論 13 33
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理背犯,服務發(fā)現(xiàn)坏瘩,斷路器,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • 一漠魏、認識Hystrix Hystrix是Netflix開源的一款容錯框架倔矾,包含常用的容錯方法:線程池隔離、信號量隔...
    新棟BOOK閱讀 4,033評論 0 19
  • 一柱锹、認識Hystrix Hystrix是Netflix開源的一款容錯框架哪自,包含常用的容錯方法:線程池隔離、信號量隔...
    新棟BOOK閱讀 26,466評論 1 37
  • 前言 分布式系統(tǒng)中經(jīng)常會出現(xiàn)某個基礎服務不可用造成整個系統(tǒng)不可用的情況, 這種現(xiàn)象被稱為服務雪崩效應. 為了應對服...
    簡約生活owen閱讀 901評論 0 6