深入理解Hystrix之文件翻譯

方誌朋發表於2017-07-24

轉載請標明出處:
blog.csdn.net/forezp/arti…
本文出自方誌朋的部落格

什麼是Hystrix

在分散式系統中,服務與服務之間依賴錯綜複雜,一種不可避免的情況就是某些服務將會出現失敗。Hystrix是一個庫,它提供了服務與服務之間的容錯功能,主要體現在延遲容錯和容錯,從而做到控制分散式系統中的聯動故障。Hystrix通過隔離服務的訪問點,阻止聯動故障,並提供故障的解決方案,從而提高了這個分散式系統的彈性。

Hystrix解決了什麼問題

在複雜的分散式系統中,可能有成百上千個依賴服務,這些服務由於某種故障,比如機房的不可靠性、網路服務商的不可靠性等因素,導致某個服務不可用,如果系統不隔離該不可用的服務,可能會導致整個系統不可用。

例如,對於依賴30個服務的應用程式,每個服務的正常執行時間為99.99%,這是您期望的

99.9930 = 99.7%的正常執行時間
10億次請求中有0.3%= 3,000,000次失敗
2小時停機時間/月,即使所有的依賴都有很好的正常執行時間。

實際情況可能比這更糟糕。

如果不設計整個系統的韌性,即使所有依賴關係表現良好,即使0.01%的停機時間對數十個服務中的每一個服務的總體影響等同於每個月停機的潛在時間。

當所以的服務都出UP狀態,即Ok狀態,一個請求流程可能是這樣:

當某一個服務出現了延遲,可能會阻止整個該請求:

在高併發的情況下,單個服務的延遲,可能導致所有的請求都處於延遲狀態,可能在幾秒鐘就使服務處於負載飽和的狀態。

服務的單個點的請求故障,會導致整個服務出現故障,更為糟糕的是該故障服務,會導致其他的服務出現負載飽和,資源耗盡,直到不可用,從而導致這個分散式系統都不可用。這就是“雪崩”。

當通過第三方客戶端執行網路訪問時,這些問題會加劇。第三方客戶就是一個“黑匣子”,其中實施細節被隱藏,並且可以隨時更改,網路或資源配置對於每個客戶端庫都是不同的,通常難以監視和 更改。

通過的故障包括:

網路連線失敗或降級。 服務和伺服器失敗或變慢。 新的庫或服務部署會改變行為或效能特徵。 客戶端庫有錯誤。

所有這些都代表需要隔離和管理的故障和延遲,以便單個故障依賴關係不能導致整個應用程式或系統的故障。

Hystrix的設計原則

原則如下:

  • 防止單個服務的故障,耗盡整個系統服務的容器(比如tomcat)的執行緒資源。
  • 減少負載並快速失敗,而不是排隊。
  • 在可行的情況下提供回退以保護使用者免受故障。
  • 使用隔離技術(如隔板,泳道和斷路器模式)來限制任何一個依賴的影響。
  • 通過近乎實時的指標,監控和警報來優化發現故障的時間。
  • 通過配置更改的低延遲傳播優化恢復時間,並支援Hystrix大多數方面的動態屬性更改,從而允許您使用低延遲反饋迴圈進行實時操作修改。
  • 保護整個依賴客戶端執行中的故障,而不僅僅是在網路流量上進行保護降級、限流。

Hystrix 是怎麼實現它的設計目標的?

  • 通過HystrixCommand 或者HystrixObservableCommand 將所有的外部系統(或者稱為依賴)包裝起來,整個包裝物件是單獨執行在一個執行緒之中(這是典型的命令模式)。
  • 超時請求應該超過你定義的閾值
  • 為每個依賴關係維護一個小的執行緒池(或訊號量); 如果它變滿了,那麼依賴關係的請求將立即被拒絕,而不是排隊等待。
  • 統計成功,失敗(由客戶端丟擲的異常),超時和執行緒拒絕。
  • 開啟斷路器可以在一段時間內停止對特定服務的所有請求,如果服務的錯誤百分比通過閾值,手動或自動的關閉斷路器。
  • 當請求被拒絕、連線超時或者斷路器開啟,直接執行fallback邏輯。
  • 近乎實時監控指標和配置變化。

當您使用Hystrix包裝每個底層依賴項時,上圖所示的體系結構如下圖所示。 每個依賴關係彼此隔離,在延遲發生時可以飽和的資源受到限制,迅速執行fallback的邏輯,該邏輯決定了在依賴關係中發生任何型別的故障時會做出什麼響應:

Hystrix是怎麼工作的?

架構圖

下圖顯示通過Hystrix向服務依賴關係發出請求時會發生什麼:

具體將從以下幾個方面進行描述:

1.構建一個HystrixCommand或者HystrixObservableCommand 物件。

第一步是構建一個HystrixCommand或HystrixObservableCommand物件來表示你對依賴關係的請求。 其中建構函式需要和請求時的引數一致。

構造HystrixCommand物件,如果依賴關係預期返回單個響應。 可以這樣寫:

HystrixCommand command = new HystrixCommand(arg1, arg2);複製程式碼

同理,可以構建HystrixObservableCommand :

HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);複製程式碼

2.執行Command

通過使用Hystrix命令物件的以下四種方法之一,可以執行該命令有四種方法(前兩種方法僅適用於簡單的HystrixCommand物件,並不適用於HystrixObservableCommand):

  • execute()--阻塞,,然後返回從依賴關係接收到的單個響應(或者在發生錯誤時丟擲異常)
  • queue()--返回一個可以從依賴關係獲得單個響應的future 物件
  • observe()--訂閱Observable代表依賴關係的響應,並返回一個Observable,該Observable會複製該來源Observable
  • toObservable() --返回一個Observable,當您訂閱它時,將執行Hystrix命令併發出其響應
K             value   = command.execute();
Future<K>     fValue  = command.queue();
Observable<K> ohValue = command.observe();         
Observable<K> ocValue = command.toObservable();複製程式碼

同步呼叫execute()呼叫queue().get(). queue()依次呼叫toObservable().toBlocking().toFuture()。 這就是說,最終每個HystrixCommand都由一個Observable實現支援,甚至是那些旨在返回單個簡單值的命令。

3.響應是否有快取?

如果為該命令啟用請求快取,並且如果快取中對該請求的響應可用,則此快取響應將立即以“可觀察”的形式返回。

4.斷路器是否開啟?

當您執行該命令時,Hystrix將檢查斷路器以檢視電路是否開啟。

如果電路開啟(或“跳閘”),則Hystrix將不會執行該命令,但會將流程路由到(8)獲取回退。

如果電路關閉,則流程進行到(5)以檢查是否有可用於執行命令的容量。

5.執行緒池/佇列/訊號量是否已經滿負載?

如果與命令相關聯的執行緒池和佇列(或訊號量,如果不線上程中執行)已滿,則Hystrix將不會執行該命令,但將立即將流程路由到(8)獲取回退。

6.HystrixObservableCommand.construct() 或者 HystrixCommand.run()

在這裡,Hystrix通過您為此目的編寫的方法呼叫對依賴關係的請求,其中之一是:

  • HystrixCommand.run() - 返回單個響應或者引發異常

  • HystrixObservableCommand.construct() - 返回一個發出響應的Observable或者傳送一個onError通知

如果run()或construct()方法超出了命令的超時值,則該執行緒將丟擲一個TimeoutException(或者如果命令本身沒有在自己的執行緒中執行,則會產生單獨的計時器執行緒)。 在這種情況下,Hystrix將響應通過8進行路由。獲取Fallback,如果該方法不取消/中斷,它會丟棄最終返回值run()或construct()方法。

請注意,沒有辦法強制潛線上程停止工作 - 最好的Hystrix可以在JVM上執行它來丟擲一個InterruptedException。 如果由Hystrix包裝的工作不處理InterruptedExceptions,Hystrix執行緒池中的執行緒將繼續工作,儘管客戶端已經收到了TimeoutException。 這種行為可能使Hystrix執行緒池飽和,儘管負載“正確地流失”。 大多數Java HTTP客戶端庫不會解釋InterruptedExceptions。 因此,請確保在HTTP客戶端上正確配置連線和讀/寫超時。

如果該命令沒有引發任何異常並返回響應,則Hystrix在執行某些日誌記錄和度量報告後返回此響應。 在run()的情況下,Hystrix返回一個Observable,發出單個響應,然後進行一個onCompleted通知; 在construct()的情況下,Hystrix返回由construct()返回的相同的Observable。

7.計算Circuit 的健康

Hystrix向斷路器報告成功,失敗,拒絕和超時,該斷路器維護了一系列的計算統計資料組。

它使用這些統計資訊來確定電路何時“跳閘”,此時短路任何後續請求直到恢復時間過去,在首次檢查某些健康檢查之後,它再次關閉電路。

8.獲取Fallback

當命令執行失敗時,Hystrix試圖恢復到你的回退:當construct()或run()(6.)丟擲異常時,當命令由於電路斷開而短路時(4.),當 命令的執行緒池和佇列或訊號量處於容量(5.),或者當命令超過其超時長度時。

編寫Fallback ,它不一依賴於任何的網路依賴,從記憶體中獲取獲取通過其他的靜態邏輯。如果你非要通過網路去獲取Fallback,你可能需要些在獲取服務的介面的邏輯上寫一個HystrixCommand。

9.返回成功的響應

如果 Hystrix command成功,如果Hystrix命令成功,它將以Observable的形式返回對呼叫者的響應或響應。 根據您在上述步驟2中呼叫命令的方式,此Observable可能會在返回給您之前進行轉換:

  • execute() - 以與.queue()相同的方式獲取Future,然後在此Future上呼叫get()來獲取Observable發出的單個值
  • queue() - 將Observable轉換為BlockingObservable,以便將其轉換為Future,然後返回此未來
  • observe() - 立即訂閱Observable並啟動執行命令的流程; 返回一個Observable,當您訂閱它時,重播排放和通知
  • toObservable() - 返回Observable不變; 您必須訂閱它才能實際開始導致命令執行的流程

斷路器(Circuit Breaker)

下圖顯示HystrixCommand或HystrixObservableCommand如何與HystrixCircuitBreaker及其邏輯和決策流程進行互動,包括計數器在斷路器中的行為。

發生電路開閉的過程如下:

1.假設電路上的音量達到一定閾值(HystrixCommandProperties.circuitBreakerRequestVolumeThreshold())...

2.並假設錯誤百分比超過閾值錯誤百分比(HystrixCommandProperties.circuitBreakerErrorThresholdPercentage())...

3.然後斷路器從CLOSED轉換到OPEN。

4.雖然它是開放的,它使所有針對該斷路器的請求短路。

5.經過一段時間(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()),下一個單個請求是通過(這是HALF-OPEN狀態)。 如果請求失敗,斷路器將在睡眠視窗持續時間內返回到OPEN狀態。 如果請求成功,斷路器將轉換到CLOSED,邏輯1.重新接管。

隔離(Isolation)

Hystrix採用隔板模式來隔離彼此的依賴關係,並限制對其中任何一個的併發訪問。

關注我的公眾號

精彩內容不能錯過!

forezp.jpg
forezp.jpg

相關文章