天天看點

Hystrix 配置參數全解析

前言

不久前在部門周會上分享了 Hystrix 源碼解析之後,就無奈地背上了專家包袱,同僚們都認為我對 Hystrix 很熟,我們接觸 Hystrix 更多的還是工作中的使用和配置,是以很多人一遇到 Hystrix 的配置問題就會過來問我。為了不讓他們失望,我把 Hystrix 的 配置文檔 仔細看了一遍,将有疑問的點通過翻源碼、查官方 issue、自己實驗的方式整理了一遍,這才對 Hystrix 的配置有了一定的了解。

在了解這些配置項的過程中,我也發現了很多坑,平常我們使用中認為理所應當的值并不會讓 Hystrix 如期望工作,沒有經過斟酌就複制粘貼的配置會讓 Hystrix 永遠不會起作用。于是寫下本文,希望能幫助小夥伴們掌握 Hystrix。

HystrixCommand

配置方式

我們的配置都是基于 HystrixCommand 的,我們通過在方法上添加

@HystrixCommand

注解并配置注解的參數來實作配置,但有的時候一個類裡面會有多個 Hystrix 方法,每個方法都是類似配置的話會備援很多代碼,這時候我們可以在類上使用

@DefaultProperties

注解來給整個類的 Hystrix 方法設定一個預設值。

配置項

下面是 HystrixCommand 支援的參數,除了

commandKey/observableExecutionMode/fallbackMethod

外,都可以使用

@DefaultProperties

配置預設值。

  • commandKey:用來辨別一個 Hystrix 指令,預設會取被注解的方法名。需要注意:

    Hystrix 裡同一個鍵的唯一辨別并不包括 groupKey

    ,建議取一個獨一二無的名字,防止多個方法之間因為鍵重複而互相影響。
  • groupKey:一組 Hystrix 指令的集合, 用來統計、報告,預設取類名,可不配置。
  • threadPoolKey:用來辨別一個線程池,

    如果沒設定的話會取 groupKey

    ,很多情況下都是同一個類内的方法在共用同一個線程池,如果兩個共用同一線程池的方法上配置了同樣的屬性,在第一個方法被執行後線程池的屬性就固定了,是以屬性會以第一個被執行的方法上的配置為準。
  • commandProperties:與此指令相關的屬性。
  • threadPoolProperties:與線程池相關的屬性,
  • observableExecutionMode:當 Hystrix 指令被包裝成 RxJava 的 Observer 異步執行時,此配置指定了 Observable 被執行的模式,預設是

    ObservableExecutionMode.EAGER

    ,Observable 會在被建立後立刻執行,而

    ObservableExecutionMode.EAGER

    模式下,則會産生一個 Observable 被 subscribe 後執行。我們常見的指令都是同步執行的,此配置項可以不配置。
  • ignoreExceptions:預設 Hystrix 在執行方法時捕獲到異常時執行回退,并統計失敗率以修改熔斷器的狀态,而被忽略的異常則會直接抛到外層,不會執行回退方法,也不會影響熔斷器的狀态。
  • raiseHystrixExceptions:當配置項包括

    HystrixRuntimeException

    時,所有的未被忽略的異常都會被包裝成 HystrixRuntimeException,配置其他種類的異常好像并沒有什麼影響。
  • fallbackMethod:方法執行時熔斷、錯誤、逾時時會執行的回退方法,需要保持此方法與 Hystrix 方法的簽名和傳回值一緻。
  • defaultFallback:預設回退方法,當配置 fallbackMethod 項時此項沒有意義,另外,預設回退方法不能有參數,傳回值要與 Hystrix方法的傳回值相同。

commandProperties

Hystrix 的指令屬性是由

@HystrixProperty

注解數組構成的,HystrixProperty 由 name 和 value 兩個屬性,資料類型都是字元串。

以下将所有的指令屬性分組來介紹。

微服務項目to    fhadmin.cn

線程隔離(Isolation)

  • execution.isolation.strategy: 配置請求隔離的方式,有 threadPool(線程池,預設)和 semaphore(信号量)兩種,信号量方式高效但配置不靈活,我們一般采用 Java 裡常用的線程池方式。
  • execution.timeout.enabled:是否給方法執行設定逾時,預設為 true。
  • execution.isolation.thread.timeoutInMilliseconds:方法執行逾時時間,預設值是 1000,即 1秒,此值根據業務場景配置。
  • execution.isolation.thread.interruptOnTimeout: execution.isolation.thread.interruptOnCancel:是否在方法執行逾時/被取消時中斷方法。需要注意在 JVM 中我們無法強制中斷一個線程,如果 Hystrix 方法裡沒有進行中斷信号的邏輯,那麼中斷會被忽略。
  • execution.isolation.semaphore.maxConcurrentRequests:預設值是 10,此配置項要在

    execution.isolation.strategy

    配置為

    semaphore

    時才會生效,它指定了一個 Hystrix 方法使用信号量隔離時的最大并發數,超過此并發數的請求會被拒絕。信号量隔離的配置就這麼一個,也是前文說信号量隔離配置不靈活的原因。

統計器(Metrics)

滑動視窗

: Hystrix 的統計器是由滑動視窗來實作的,我們可以這麼來了解滑動視窗:一位乘客坐在正在行駛的列車的靠窗座位上,列車行駛的公路兩側種着一排挺拔的白楊樹,随着列車的前進,路邊的白楊樹迅速從視窗滑過,我們用每棵樹來代表一個請求,用列車的行駛代表時間的流逝,那麼,列車上的這個視窗就是一個典型的滑動視窗,這個乘客能通過視窗看到的白楊樹就是 Hystrix 要統計的資料。

: bucket 是 Hystrix 統計滑動視窗資料時的最小機關。同樣類比列車視窗,在列車速度非常快時,如果每掠過一棵樹就統計一次視窗内樹的資料,顯然開銷非常大,如果乘客将視窗分成十分,列車前進行時每掠過視窗的十分之一就統計一次資料,開銷就完全可以接受了。 Hystrix 的 bucket (桶)也就是視窗 N分之一 的概念。

  • metrics.rollingStats.timeInMilliseconds:此配置項指定了視窗的大小,機關是 ms,預設值是 1000,即一個滑動視窗預設統計的是 1s 内的請求資料。
  • metrics.healthSnapshot.intervalInMilliseconds:它指定了健康資料統計器(影響 Hystrix 熔斷)中每個桶的大小,預設是 500ms,在進行統計時,Hystrix 通過

    metrics.rollingStats.timeInMilliseconds / metrics.healthSnapshot.intervalInMilliseconds

    計算出桶數,在視窗滑動時,每滑過一個桶的時間間隔時就統計一次目前視窗内請求的失敗率。
  • metrics.rollingStats.numBuckets:Hystrix 會将指令執行的結果類型都統計彙總到一塊,給上層應用使用或生成統計圖表,此配置項即指定了,生成統計資料流時滑動視窗應該拆分的桶數。此配置項最易跟上面的

    metrics.healthSnapshot.intervalInMilliseconds

    搞混,認為此項影響健康資料流的桶數。 此項預設是 10,并且需要保持此值能被

    metrics.rollingStats.timeInMilliseconds

    整除。
  • metrics.rollingPercentile.enabled:是否統計方法響應時間百分比,預設為 true 時,Hystrix 會統計方法執行的

    1%,10%,50%,90%,99%

    等比例請求的平均耗時用以生成統計圖表。
  • metrics.rollingPercentile.timeInMilliseconds:統計響應時間百分比時的視窗大小,預設為 60000,即一分鐘。
  • metrics.rollingPercentile.numBuckets:統計響應時間百分比時滑動視窗要劃分的桶用,預設為6,需要保持能被

    metrics.rollingPercentile.timeInMilliseconds

  • metrics.rollingPercentile.bucketSize:統計響應時間百分比時,每個滑動視窗的桶内要保留的請求數,桶内的請求超出這個值後,會覆寫最前面儲存的資料。預設值為 100,在統計響應百分比配置全為預設的情況下,每個桶的時間長度為 10s = 60000ms / 6,但這 10s 内隻保留最近的 100 條請求的資料。

熔斷器(Circuit Breaker)

  • circuitBreaker.enabled:是否啟用熔斷器,預設為 true;
  • circuitBreaker.forceOpen: circuitBreaker.forceClosed:是否強制啟用/關閉熔斷器,強制啟用關閉都想不到什麼應用的場景,保持預設值,不配置即可。
  • circuitBreaker.requestVolumeThreshold:啟用熔斷器功能視窗時間内的最小請求數。試想如果沒有這麼一個限制,我們配置了 50% 的請求失敗會打開熔斷器,視窗時間内隻有 3 條請求,恰巧兩條都失敗了,那麼熔斷器就被打開了,5s 内的請求都被快速失敗。此配置項的值需要根據接口的 QPS 進行計算,值太小會有誤打開熔斷器的可能,值太大超出了時間視窗内的總請求數,則熔斷永遠也不會被觸發。建議設定為

    QPS * 視窗秒數 * 60%

  • circuitBreaker.errorThresholdPercentage:在通過滑動視窗擷取到目前時間段内 Hystrix 方法執行的失敗率後,就需要根據此配置來判斷是否要将熔斷器打開了。 此配置項預設值是 50,即視窗時間内超過 50% 的請求失敗後會打開熔斷器将後續請求快速失敗。
  • circuitBreaker.sleepWindowInMilliseconds:熔斷器打開後,所有的請求都會快速失敗,但何時服務恢複正常就是下一個要面對的問題。熔斷器打開時,Hystrix 會在經過一段時間後就放行一條請求,如果這條請求執行成功了,說明此時服務很可能已經恢複了正常,那麼會将熔斷器關閉,如果此請求執行失敗,則認為服務依然不可用,熔斷器繼續保持打開狀态。此配置項指定了熔斷器打開後經過多長時間允許一次請求嘗試執行,預設值是 5000。

其他(Context/Fallback)

  • requestCache.enabled:是否啟用請求結果緩存。預設是 true,但它并不意味着我們的每個請求都會被緩存。緩存請求結果和從緩存中擷取結果都需要我們配置

    cacheKey

    ,并且在方法上使用

    @CacheResult

    注解聲明一個緩存上下文。
  • requestLog.enabled:是否啟用請求日志,預設為 true。
  • fallback.enabled:是否啟用方法回退,預設為 true 即可。
  • fallback.isolation.semaphore.maxConcurrentRequests:回退方法執行時的最大并發數,預設是10,如果大量請求的回退方法被執行時,超出此并發數的請求會抛出

    REJECTED_SEMAPHORE_FALLBACK

    異常。

threadPoolProperties

線程池的配置也是由 HystrixProperty 數組構成,配置方式與指令屬性一緻。

  • coreSize:核心線程池的大小,預設值是 10,一般根據

    QPS * 99% cost + redundancy count

    計算得出。
  • allowMaximumSizeToDivergeFromCoreSize:是否允許線程池擴充到最大線程池數量,預設為 false;
  • maximumSize:線程池中線程的最大數量,預設值是 10,此配置項單獨配置時并不會生效,需要啟用

    allowMaximumSizeToDivergeFromCoreSize

    項。
  • maxQueueSize:作業隊列的最大值,預設值為 -1,設定為此值時,隊列會使用

    SynchronousQueue

    ,此時其 size 為0,Hystrix 不會向隊列記憶體放作業。如果此值設定為一個正的 int 型,隊列會使用一個固定 size 的

    LinkedBlockingQueue

    ,此時在核心線程池内的線程都在忙碌時,會将作業暫時存放在此隊列内,但超出此隊列的請求依然會被拒絕。
  • queueSizeRejectionThreshold:由于

    maxQueueSize

    值線上程池被建立後就固定了大小,如果需要動态修改隊列長度的話可以設定此值,即使隊列未滿,隊列内作業達到此值時同樣會拒絕請求。此值預設是 5,是以有時候隻設定了

    maxQueueSize

    也不會起作用。
  • keepAliveTimeMinutes:由上面的

    maximumSize

    ,我們知道,線程池核心心線程數目都在忙碌,再有新的請求到達時,線程池容量可以被擴充為到最大數量,等到線程池空閑後,多于核心數量的線程還會被回收,此值指定了線程被回收前的存活時間,預設為 2,即兩分鐘。

工作方式

Hystrix 内線程池的使用是基于 Java 内置線程池的簡單包裝,通常有以下三種狀态:

  • 如果請求量少,達不到 coreSize,通常會使用核心線程來執行任務。
  • 如果設定了

    maxQueueSize

    ,當請求數超過了 coreSize, 通常會把請求放到 queue 裡,待核心線程有空閑時消費。
  • 如果 queue 長度無法存儲請求,則會建立新線程執行直到達到

    maximumSize

    最大線程數,多出核心線程數的線程會在空閑時回收。