Hystrix原理分析

一、容錯限流的需求

在複雜的分佈式系統中通常有很多依賴,如果一個應用不能對來自依賴故障進行隔離,那麼應用本身就處於被拖垮的風險中。在一個高流量的網站中,某一個單一後端一旦發生延遲,將會在數秒內導致所有的應用資源被耗盡,這也就是我們常說的雪崩效應。

比如在電商系統的下單業務中,在訂單服務創建訂單後同步調用庫存服務進行庫存的扣減,假如庫存服務出現了故障,那麼會導致下單請求線程會被阻塞,當有大量的下單請求時,則會佔滿應用連接數從而導致訂單服務無法對外提供服務。

二、容錯限流的原理

對於基本的容錯限流模式,主要有以下幾點需要考量:

  • 主動超時:在調用依賴時儘快的超時,可以設置比較短的超時時間,比如2s,防止長時間的等待。
  • 限流:限制最大併發數。
  • 熔斷:錯誤數達到閾值時,類似於保險絲熔斷。
  • 隔離:隔離不同的依賴調用
  • 服務降級:資源不足時進行服務降級

1.斷路器模式

實現流程爲:當斷路器的開關爲關閉時(對應圖中的綠色),每次請求進來都是成功的,當後端服務出現問題,請求出現的錯誤數達到一定的閾值,則會觸發斷路器爲打開狀態(對應圖中的紅色),在斷路器爲打開狀態時,進來的所有請求都會被拒絕,當然也不是一直會拒絕請求,而是彈性的,過了特定的時間後,斷路器會進入半打開狀態(對應圖中的黃色),這是會讓一部分請求通過進行嘗試,如果嘗試還是有問題,則繼續進入打開狀態,如果嘗試沒有問題了,則會進入關閉狀態。

2.艙壁隔離模式

艙壁隔離模式可以對資源進行隔離,類似於船的船艙都是被隔離開來的,當其中一個或者幾個船艙出現問題,比如漏水,是不會影響到其他的船艙的,從而實現一種資源隔離的效果。

3.容錯理念

  • 凡是依賴都有可能會失敗。
  • 凡是資源都有限制,比如CPU、Memory、Threads、Queue。
  • 網絡並不可靠,可能存在網絡抖動等其他問題。
  • 延遲是應用穩定的殺手,延遲會佔據大量的資源。

三、什麼是Hystrix

Hystrix是Netflix公司開源的一款容錯框架。 它可以完成以下幾件事情:

  • 資源隔離,包括線程池隔離和信號量隔離,避免某個依賴出現問題會影響到其他依賴。
  • 斷路器,當請求失敗率達到一定的閾值時,會打開斷路器開關,直接拒絕後續的請求,並且具有彈性機制,在後端服務恢復後,會自動關閉斷路器開關。
  • 降級回退,當斷路器開關被打開,服務調用超時/異常,或者資源不足(線程、信號量)會進入指定的fallback降級方法。
  • 請求結果緩存,hystrix實現了一個內部緩存機制,可以將請求結果進行緩存,那麼對於相同的請求則會直接走緩存而不用請求後端服務。
  • 請求合併, 可以實現將一段時間內的請求合併,然後只對後端服務發送一次請求。

四、Hystrix核心概念

1.資源隔離

資源隔離的思想參考上述的艙壁隔離模式,在hystrix中提供了兩種資源隔離策略:線程池隔離、信號量隔離。

線程池隔離:線程池隔離會爲每一個依賴創建一個線程池來處理來自該依賴的請求,不同的依賴線程池相互隔離,就算依賴A出故障,導致線程池資源被耗盡,也不會影響其他依賴的線程池資源。

  • 優點:支持排隊和超時,支持異步調用。
  • 缺點:線程的創建一個調度會造成一定的性能開銷。
  • 適用場景:適合耗時較長的接口場景,比如接口處理邏輯複雜,且與第三方中間件有交互,因爲線程池模式的請求線程與實際轉發線程不是同一個,所以可以保證容器有足夠的線程來處理新的請求。

信號量隔離模式: 初始化信號量currentCount=0,每進來一個請求需要先將currentCount自增,再判斷currentCount的值是否小於系統最大信號量,小於則繼續執行,大於則直接返回,拒絕請求。

代碼如下:

public boolean tryAcquire() {
    int currentCount = this.count.incrementAndGet();
    if (currentCount > (Integer)this.numberOfPermits.get()) {
        this.count.decrementAndGet();
        return false;
    } else {
        return true;
    }
}
  • 優點:輕量,無額外的開銷,只是一個簡單的計數器
  • 缺點:不支持任務排隊和主動超時;不支持異步調用
  • 適用場景:適合能快速響應的接口場景,不適合一些耗時較長的接口場景,因爲信號量模式下的請求線程與轉發處理線程是同一個,如果接口耗時過長有可能會佔滿容器的線程數。
隔離方式 是否支持超時 是否支持熔斷 隔離原理 是否異步調用 資源消耗
線程池隔離 支持,可直接返回 支持,當線程池到達maxSize後,再請求會觸發fallback接口進行熔斷 每個服務單獨用線程池,請求線程與轉發處理線程不是同一個 可以是異步,也可以是同步。看調用的方法 大,大量線程的上下文切換,容易造成機器負載高
信號量隔離 不支持,如果阻塞,只能通過調用協議(如:socket超時才能返回) 支持,當信號量達到maxConcurrentRequests後。再請求會觸發fallback 通過信號量的計數器,請求線程與轉發處理線程是同一個 同步調用,不支持異步 小,只是個計數器

2.斷路器

斷路器工作原理如下:

Hystrix是基於滾筒式來處理,每一秒會產生一個buckets,每產生一個新的buckets就會移除一個最老的buckets,默認是10秒一個窗口。buckets在內存中就是一種數據結構,每個buckets會記錄Metrics的相關數據,比如成功、失敗、超時、拒絕。

當一個HystrixCommand進來後,會先通過allowRequest()方法判斷是否允許通過該次請求,allowRequest()方法會通過isOpen判斷斷路器是否打開。斷路器關閉,則允許通過該次請求;斷路器打開,則會判斷是否過了睡眠週期。沒有過睡眠週期則返回false,拒絕通過該次請求,過了睡眠週期則會嘗試放行。

isOpen()方法會按照(failure) / (success+failure)公式計算出失敗率,如果失敗率大於閾值,則會觸發熔斷。公式中的成功、失敗的數據就來源於每10秒中一個窗口的滾筒數據。

對於一個依賴調用,要麼調用成功,要麼調用失敗(包括異常、超時、拒絕),這些調用結果都會記錄到buckets中。對於調用成功結果來說,還會判斷斷路器開關是否打開,如果是打開狀態的話,則會關閉斷路器並重置相關的計數器。

3.降級回退

降級,通常指事務高峯期,爲了保證核心服務正常運行,需要停掉一些不太重要的業務,或者某些服務不可用時,執行備用邏輯從故障服務中快速失敗或快速返回,以保障主體業務不受影響。 Hystrix提供的降級主要是爲了容錯,保證當前服務不受依賴服務故障的影響,從而提高服務的健壯性。

1)哪些情況會進入降級邏輯

  • 斷路器打開
  • 線程池/信號量資源不足
  • 執行依賴調用超時
  • 執行依賴調用異常

2)降級回退方式

(1)Fail Fast快速失敗

快速失敗是最普通的命令執行方法,命令沒有重寫降級邏輯。 如果命令執行發生任何類型的故障,它將直接拋出異常。

(2)Fail Fast無聲失敗

指在降級方法中通過返回null,空Map,空List或其他類似的響應來完成。

(3)FallBack:Static

指在降級方法中返回靜態默認值。 這不會導致服務以“無聲失敗”的方式被刪除,而是導致默認行爲發生。如:應用根據命令執行返回true / false執行相應邏輯,但命令執行失敗,則默認爲true。

(4)FallBack:Stubbed

當命令返回一個包含多個字段的複合對象時,適合以Stubbed 的方式回退。

(5)FallBack:Cache via Network

有時,如果調用依賴服務失敗,可以從緩存服務(如redis)中查詢舊數據版本。由於又會發起遠程調用,所以建議重新封裝一個Command,使用不同的ThreadPoolKey,與主線程池進行隔離。

(6)Primary+Secondary with FallBack

有時系統具有兩種行爲- 主要和次要,或主要和故障轉移。主要和次要邏輯涉及到不同的網絡調用和業務邏輯,所以需要將主次邏輯封裝在不同的Command中,使用線程池進行隔離。爲了實現主從邏輯切換,可以將主次command封裝在外觀HystrixCommand的run方法中,並結合配置中心設置的開關切換主從邏輯。由於主次邏輯都是經過線程池隔離的HystrixCommand,因此外觀HystrixCommand可以使用信號量隔離,而沒有必要使用線程池隔離引入不必要的開銷。

4.請求結果緩存


實際應用場景很少,不予過多介紹。

5.請求合併


實際應用場景很少,不予過多介紹。

五、Hystrix工作流程

對於一次依賴調用,會被封裝在一個HystrixCommand對象中,調用的執行有兩種方式,一種是調用execute()方法同步調用,另一種是調用queue()方法進行異步調用。

執行時會判斷斷路器開關是否打開,如果斷路器打開,則進入getFallback()降級邏輯;如果斷路器關閉,則判斷線程池/信號量資源是否已滿,如果資源滿了,則進入getFallback()降級邏輯;如果沒滿,則執行run()方法。再判斷執行run()方法是否超時,超時則進入getFallback()降級邏輯,run()方法執行失敗,則進入getFallback()降級邏輯,執行成功則報告Metrics。Metrics中的數據包括執行成功、超時、失敗等情況的數據,Hystrix會計算一個斷路器的健康值,也就是失敗率,當失敗率超過閾值後則會觸發斷路器開關打開。

getFallback()邏輯爲:如果沒有實現fallback()方法,則直接拋出異常,另外fallback降級也是需要資源的,在fallback時需要獲取一個針對fallback的信號量,只有獲取成功才能fallback,獲取信號量失敗,則拋出異常,獲取信號量成功,纔會執行fallback方法並且會響應fallback方法中的內容。

六、參考資料

楊波老師的《微服務架構實戰160講》

https://github.com/Netflix/Hystrix/wiki

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