天天看點

Hystrix學習總結

https://kiswo.com/article/1031

導讀: 在上一篇,已經對 Hystrix 的原理進行了了解。為了加深了解和快速實際應用,編寫了一些測試代碼對幾個關鍵點進行測試驗證。有些結論,是官網已經說明的。另一些是通過測試或其他同行使用得出的結論。

通過測試代碼 hystrix-example,可以對Hystrix的細節進行分析和驗證。

1、執行Command

HystrixCommand

 的執行流程如下:

  • execute()是同步堵塞的,它調用了queue().get()方法,execute()執行完後,會建立一個新線程運作run()
  • queue()是異步非堵塞的,它調用了toObservable().toBlocking().toFuture()方法,queue()執行完後,會建立一個新線程運作run()。Future.get()是堵塞的,它等待run()運作完才傳回結果
  • observe()是異步的,是熱響應調用,它調用了toObservable().subscribe(subject)方法,observe()執行完後,會建立一個新線程運作run()。toBlocking().single()是堵塞的,需要等run()運作完才傳回結果
  • toObservable()是異步的,是冷響應調用,該方法不會主動建立線程運作run(),隻有當調用了toBlocking().single()或subscribe()時,才會去建立線程運作run()
注意:
  • 1、同一個HystrixCommand對象隻能執行一次run()
  • 2、observe()中,toBlocking().single()與subscribe()是可以共存的,因為run()是在observe()中被調用的,隻調用了一次
  • 3、toObservable()中,toBlocking().single()與subscribe()不可共存,因為run()是在toBlocking().single()或subscribe()中被調用的;如果同時存在toBlocking().single()和subscribe(),相當于調用了2次run(),會報錯

2、回退降級

  • 繼承 

    HystrixCommand

    ,重寫getFallback()方法,該方法的響應要快,盡量不要有網絡依賴
  • 如果有網絡依賴,建議采取多次降級,即在getFallback()方法執行個體化 

    HystrixCommand

    ,并執行Command
  • 注意getFallback()的異常捕捉,如果getFallback(),會直接中斷 

    HystrixCommand

     的流程

3、Command Name、Group、Thread-Pool

  • CommandKey/CommandName是一個依賴服務的command辨別
  • GroupKey是将報告,警報,儀表闆或團隊/庫所有權等指令組合在一起。一般可以根據服務子產品或第三方用戶端來配置設定GroupKey,一個GroupKey下可以有多個CommandKey
  • ThreadPoolKey用于監視,度量标準釋出,緩存和其他此類用途的HystrixThreadPool。可以一個CommandKey綁定一個ThreadPoolKey用,這樣多個線程的CommandKey就會劃分到同一個ThreadPoolKey
  • 沒有定義ThreadPoolKey時,ThreadPoolKey使用GroupKey,定義了ThreadPoolKey時,則使用定義值(采用線程政策隔離的情況下)
  • command在執行run()時,會建立一個線程,該線程的名稱是ThreadPoolKey和序列号的組合,序列号是該線程線上程池中的建立順序
  • 使用ThreadPoolKey的原因是多個command可能屬于同一個所有權或邏輯功能『組』,但某些command又需要彼此隔離。

CommandName不設定名稱的話,預設名稱是從類名派生的:

getClass().getSimpleName();
           

要明确定義名稱,通過HystrixCommand或HystrixObservableCommand構造函數傳入:

public CommandHelloWorld(String name) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")));
        this.name = name;
    }
           

或者為了儲存每個指令配置設定的Setter配置設定,你也可以像這樣緩存Setter:

private static final Setter cachedSetter = 
        Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
            .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"));    

    public CommandHelloWorld(String name) {
        super(cachedSetter);
        this.name = name;
    }
           

4、熔斷器

同時滿足以下條件,熔斷器将打開:

1、整個鍊路請求數達到閥值(

circuitBreaker.requestVolumeThreshold

),預設情況下,10秒内請求數超過20次,則符合第一個條件。

2、在滿足第一個條件的前提下,如果請求的錯誤數比例大于閥值(

circuitBreaker.errorThresholdPercentage

),則會打開熔斷器,預設為50%。

如果熔斷器處于打開狀态,将會進入休眠期,在休眠期内,所有請求都将被拒絕,直接執行fallback邏輯。

根據 

Metrics

 的計算,可以判斷熔斷器的健康狀态,進而決定是否應該關閉熔斷器:

  • 熔斷器被打開後,根據 

    circuitBreaker.sleepWindowInMilliseconds

     設定,會休眠一段時間,這段時間内的所有請求,都直接fallback
  • 休眠時間過後,Hystrix會将熔斷器狀态改為半開狀态,然後嘗試性的執行一次command,如果成功,則關閉熔斷器,如果失敗,繼續打開熔斷器,執行新的熔斷周期
  • 熔斷器打開後,熔斷器的健康檢查名額會重置,重新開始計算

熔斷器有以下幾個特殊參數:

1、如果hystrix.command.default.circuitBreaker.enabled設定為false,将不使用斷路器來跟蹤健康狀況,也不會在斷路器跳閘時将其短路(即不會執行fallback)

2、如果hystrix.command.default.circuitBreaker.forceOpen設定為true,斷路器将強制打開,所有請求将被拒絕,直接進入fallback

3、如果hystrix.command.default.circuitBreaker.forceClosed設定為true,斷路器将強制關閉,無論錯誤百分比如何,都将允許請求(永遠會執行run)
           

5、隔離政策

隔離政策分線程隔離和信号隔離。

5.1、線程隔離

HystrixCommand

 預設采用的是線程隔離政策。通過服務容錯與保護方案 — Hystrix和測試案例的 

HystrixCommandDemo1

 可以知道,當執行 

construct()

 或 

run()

 時,會建立一個線程。因為 

Hystrix

 用到了線程池,真實的流程是這樣的:

  • 1、執行 

    construct()

     或 

    run()

     時,先判斷線程池中是否有空閑的線程(每個Command都可以擁有自己的線程池而不會互相影響)
  • 2、如果沒有空閑的,則看目前線程數是否達到 

    hystrix.threadpool.default.coreSize

    ,如果達到,則需要排隊,當隊列值大于 

    hystrix.threadpool.default.maxQueueSize

    , 會拒絕請求,執行回退邏輯,如果沒有達到,則建立一個新的線程來執行
  • 3、如果有空閑的,則直接從空閑的線程中取出一個來執行

當然,我們也可以設定 

hystrix.threadpool.default.maximumSize

,動态的控制線程的大小。該參數表示一個 

HystrixCommand

 可以建立的最大線程數,當線程池中的線程在 

hystrix.threadpool.default.keepAliveTimeMinutes

時間内沒有使用,則會關閉一些線程,使線程數等于在 

hystrix.threadpool.default.coreSize

注意:

必須将 

hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize

 設定為 

true

時,

hystrix.threadpool.default.maximumSize

 才會生效

hystrix.threadpool.default.coreSize

 的預設值為10,如果需要提高此值,按照以下公式計算:

最大線程數 = QPS * 平均響應時間(機關秒)* 99% + 緩存數

舉例說明:

某個接口的單台伺服器QPS為10000,平均響應時間為20ms

最大線程數:10000 * 0.02 * 0.99 + 4 = 202
           

Hystrix

 官方建議盡量将最大線程數設定的小一些,因為它是減少負載并防止資源在延遲發生時被阻塞的主要工具。線程數能設定多大,有什麼影響,這個需要根據自身業務情況和實際壓測結果來衡量。

5.2、信号隔離

HystrixObservableCommand

 預設采用的是信号隔離。

HystrixCommand

 可以通過修改 

hystrix.command.default.execution.isolation.strategy

 參數調整為信号隔離。

  • 信号隔離是對用戶端請求線程的并發限制,采用信号隔離時,hystrix的線程相關配置将無效
  • 當請求并發量大于 

    hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests

    時,請求執行fallback
  • 當fallback的并發線程數大于

    hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests

    時,fallback将抛異常fallback execution rejected

信号隔離政策下,執行 

construct()

 或 

run()

 時,使用的是應用服務的父級線程(如Tomcat容器線程)。是以,一定要設定好并發量,有網絡開銷的調用,不建議使用該政策,容易導緻容器線程排隊堵塞,進而影響整個應用服務。

6、請求合并

  • 請求合并,可以減少通信消耗和線程數的占用,提高并發
  • 請求合并會有延遲時間窗,會帶來額外的開銷,如果請求本身有較長的延遲,或合并的請求量較多時,請求合并會提升性能,反之,可能會降低性能

7、HystrixObservableCommand

HystrixObservableCommand

 與 

HystrixCommand

 的差別:

  • 1、它們兩個是 

    Hystrix

     執行Command的兩種方式
  • 2、

    HystrixCommand

     的執行封裝在run(),fallback處理封裝在getFallBack();

    HystrixObservableCommand

     的執行封裝在contruct(),fallback處理封裝在resumeWithFallback()
  • 3、

    HystrixObservableCommand

    使用的信号隔離政策,是以,使用的是應用服務的父級線程調用contruct()
  • 4、

    HystrixObservableCommand

     在contruct()中可以定義多個onNext,當調用subscribe()注冊成功後,将依次執行這些onNext(),後者隻能在run()中傳回一個值(即一個onNext)。可以了解為 

    HystrixCommand

     一次隻能發送單條資料傳回,而

    HystrixObservableCommand

     一次可以發送多條資料傳回
  • 5、同 

    HystrixCommand

     一樣,

    HystrixObservableCommand

     使用observe(),toBlocking().single()或subscribe()可以共存,而使用toObservable(),則不能共存
參考:
  • Hystrix how to use
  • Hystrix Configuration
  • Hystrix examples
  • Hystrix常用功能介紹
  • Hystrix使用入門手冊