Hystrix

認識Hystrix

Hystrix是Netflix開源的一款容錯框架。在高併發訪問下,系統所依賴的服務的穩定性對系統掉影響非常大。依賴有很多的不可控因素,比如網絡連接慢,資源繁忙,暫時不可用等。只要有其中一個依賴服務不穩定,就會拖跨整個服務。我們要構建穩定、可靠的分佈式系統,就必須要有這樣一套容錯方法。
Hystrix提供了線程隔離、信號量隔離、熔斷、降級回退等容錯方法

Hystrix Wiki

容錯流程

隔離

線程隔離

場景:
比如我們現在有3個業務調用分別是查詢訂單、查詢商品、查詢用戶,且這三個業務請求都是依賴第三方服務-訂單服務、商品服務、用戶服務。三個服務均是通過RPC調用。當查詢訂單服務,假如線程阻塞了,這個時候後續有大量的查詢訂單請求過來,那麼容器中的線程數量則會持續增加直致CPU資源耗盡到100%,整個服務對外不可用

此時,每來一個請求(創建一個線程),阻塞在等待查詢上。併發量大,創建了很多線程阻塞在查詢訂單上,耗盡了資源。

線程隔離就是給查詢訂單服務固定大線程數量,與其他服務的線程資源區分開來。這樣就起到了隔離的作用


缺點

線程池的主要缺點就是它增加了計算的開銷,每個業務請求(被包裝成命令)在執行的時候,會涉及到請求排隊,調度和上下文切換

信號量隔離

信號量隔離的方式是限制了總的併發數,每一次請求過來,請求線程和調用依賴服務的線程是同一個線程.

總結

[圖片上傳失敗...(image-b29ee4-1548676005721)]

線程隔離用在有網絡開銷
信號量隔離用在沒有網絡開銷的地方

使用解析

public class GetOrderCircuitBreakerCommand extends HystrixCommand<String> {

    public GetOrderCircuitBreakerCommand(String name){
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ThreadPoolTestGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("testCommandKey"))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(name))
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withCircuitBreakerEnabled(true)//默認是true,本例中爲了展現該參數
                                .withCircuitBreakerForceOpen(false)//默認是false,本例中爲了展現該參數
                                .withCircuitBreakerForceClosed(false)//默認是false,本例中爲了展現該參數
                                .withCircuitBreakerErrorThresholdPercentage(5)//(1)錯誤百分比超過5%
                                .withCircuitBreakerRequestVolumeThreshold(10)//(2)10s以內調用次數10次,同時滿足(1)(2)熔斷器打開
                                .withCircuitBreakerSleepWindowInMilliseconds(5000)//隔5s之後,熔斷器會嘗試半開(關閉),重新放進來請求
                                .withExecutionTimeoutInMilliseconds(1000)
                )
                .andThreadPoolPropertiesDefaults(
                        HystrixThreadPoolProperties.Setter()
                                .withMaxQueueSize(10)   //配置隊列大小
                                .withCoreSize(2)    // 配置線程池裏的線程數
                )
        );
    }

    @Override
    protected String run() throws Exception {
        Random rand = new Random();
        //模擬錯誤百分比(方式比較粗魯但可以證明問題)
        if(1==rand.nextInt(2)){
//            System.out.println("make exception");
            throw new Exception("make exception");
        }
        return "running:  ";
    }

    @Override
    protected String getFallback() {
//        System.out.println("FAILBACK");
        return "fallback: ";
    }

    public static class UnitTest{

        public static void main(String[] args) throws InterruptedException {
            for(int i=0;i<35;i++){
                Thread.sleep(500);
                HystrixCommand<String> command = new GetOrderCircuitBreakerCommand("testCircuitBreaker");
                String result = command.execute();
                //本例子中從第11次,熔斷器開始打開
                System.out.println("call times:"+(i+1)+"   result:"+result +" isCircuitBreakerOpen: "+command.isCircuitBreakerOpen());
                //本例子中5s以後,熔斷器嘗試關閉,放開新的請求進來
            }
        }

    }
}

初始化

HystrixCommandKey

Hystrix使用單例模式存儲HystrixCommand,熔斷機制就是根據單實例上的調用情況統計實現的,所以每個HystrixCommand要有自己的名字,用於區分,同時用於依賴調用的隔離。HystrixCommandKey就是用於定義這個名字,如果沒有定義這個名字,Hystrix會使用其類名作爲其名字,可以使用HystrixCommandKey.Factory.asKey(String name)方法定義一個名稱。

HystrixThreadPoolKey

HystrixThreadPoolKey是HystrixCommand所在的線程池,如果該參數不設置則使用HystrixCommandGroupKey作爲HystrixThreadPoolKey,這種情況下同一個HystrixCommandGroupKey下的依賴調用共用同一個線程池內,如果不想共用同一個線程池,則需要設置該參數。可以使用HystrixThreadPoolKey.Factory.asKey(String name)方法設置。

HystrixCommandGroupKey

Hystrix需要對HystrixCommand進行分組,便於統計、管理,所以需要一個分組名稱,HystrixCommandGroupKey就是用於定義分組名稱,可以使用HystrixCommandGroupKey.Factory.asKey(String name)方法定義一個分組名。每個HystrixCommand必須要配置一個分組名,一個是用於分組,還有如果沒有配置HystrixThreadPoolKey,這個分組名將會用於線程池名。

HystrixCommandProperties

這個就是HystrixCommand的屬性配置,它可以設置熔斷器是否可用、熔斷器熔斷的錯誤百分比、依賴調用超時時間等,它有一些默認的配置參數,如熔斷器熔斷的錯誤百分比默認值是50%、依賴調用超時時間默認值是1000毫秒。

HystrixThreadPoolProperties

從名稱上可以看出這是線程池的屬性配置,可以通過它設置核心線程數大小、最大線程數、任務隊列大小等,當然它也又一些默認的配置參數。

命令執行

  • execute():以同步堵塞方式執行run()。調用execute()後,hystrix先創建一個新線程運行run(),接着調用程序要在execute()調用處一直堵塞着,直到run()運行完成。

  • queue():以異步非堵塞方式執行run()。調用queue()就直接返回一個Future對象,同時hystrix創建一個新線程運行run(),調用程序通過Future.get()拿到run()的返回結果,而Future.get()是堵塞執行的。

  • observe():不研究

  • toObservable():不研究

降級

使用fallback機制很簡單,繼承HystrixCommand只需重寫getFallback()

Dubbo 的服務降級策略與 Hystrix 的熔斷機制的簡單對比

一開始我有個疑問:Dubbo提供了服務降級策略,爲什麼說Hystrix對RPC(對網絡開消)的服務容錯呢?

Dubbo 提供的降級策略比較簡單,Dubbo 的降級可以通過代碼設置或者通過管理控制檯進行設置,並且一旦設置爲了降級,那麼所有消費者調用降級接口時候都是用本地 mock 值,並沒有自動恢復策略,並且必須讓管理員手動進行降級或者取消降級

Hystrix 除了提供 Dubbo 類似的降級策略外,還提供了自適應的降級恢復策略,當調用服務異常次數超過指定閾值後會自動進行降級,並且降級後不是一直都是用降級方案,它可以在降級後時間窗口過後再次進行嘗試遠程調用,如果目前服務已經 OK,那麼會自動關閉降級,這種自反饋的降級,降級恢復策略在整個過程中都自動完成的,不需要人工干涉。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章