天天看點

springcloud(十):Hystrix工作流程分析

通過Netflix Hystrix官方公布的流程圖,我們來了解一下Hystrix的工作流程

首先建立一個HystrixCommand對象或者HystrixObservableCommand對象用來表示對依賴服務的操作請求,同時傳遞所有需要的參數,它采用了指令模式來實作對服務調用操作的封裝,這兩個Command對象分别針對不同的應用場景。

HystrixCommand:依賴服務每次傳回單一的回應。

HystrixObservableCommand:若期望依賴服務傳回一個 Observable,并應用Observer模式監聽依賴服務的回應,用在依賴的服務傳回多個操作結果的時候。

指令模式

關于指令模式,将請求封裝成對象,使用不同的請求、隊列,或者日志請求來參數化其他對象,就是指令模式。指令模式也可以支援撤銷操作。當需要将送出請求的對象和執行請求的對象解耦的時候,可以考慮使用指令模式。

舉個例子,什麼是指令,比如說打開燈就是一個指令,我們可以将這個指令命名為LightOnCommand,上面說的将請求封裝成對象的意思就是說将打開燈這個請求(指令),封裝成LightOnCommand這一對象,但是這個LightOnCommand指令肯定不能自己執行,要确定是誰被執行這個指令,而被執行這個指令的對象就是燈,此時可以把這個燈,比如說節能燈CellingLight對象注入到指令中,在LightOnCommand指令對象中添加一個execute()方法,用來執行light.on()(打開燈)的操作。上面說了指令對象、被執行者(或者叫被指令者)對象,還差一個執行指令的對象,這裡可以是一個遙控器,将LightOnCommand對象注入進來,在執行指令的時候,我們就可以直接調用這個指令對象就好了,而不需要知道我的這個指令到底是哪一個具體的燈去執行,這樣就達到了程式上的解耦。當然我們除了存在execute()方法做為下一步執行的操作方法,還可以提供一個undo()的撤銷方法。

 指令模式的要點:

指令模式将送出請求的對象和執行請求的對象解耦;

在被解耦的兩者之間是通過指令對象進行溝通的。指令對象封裝了接收者和一個或一組動作;

調用者通過調用指令對象的execute()方法送出請求,這會使得接收者的動作被調用;

調用者可以接受指令當作參數,甚至在運作時動态地進行;

指令可以支援撤銷,做法是實作一個undo()方法來回到execute()方法被執行前的狀态;

從圖中看到一共有四種命名的執行方式,其中HystrixCommand實作了這四個執行方式,但主要是使用前兩個:

execute():同步執行,從依賴的服務傳回一個單一的結果對象,或者是在發生錯誤的時候抛出異常。

queue():異步執行,直接傳回一個Future對象,其中包含了服務執行結束時要傳回的單一結果對象。

R value = command.execute();

Future<R> fValue = command.queue();

HystrixObservableCommand實作了另外兩種執行方式。

observe():傳回Observable對象,傳回 Observable 對象,立即送出請求,在依賴服務響應(或者抛出異常/逾時)時,通過注冊的 Subscriber 得到傳回結果,它是一個Hot Observable。

toObservable():傳回Observable對象,但隻有在訂閱該對象時,才會送出請求,然後在依賴服務響應(或者抛出異常/逾時)時,通過注冊的 Subscriber 得到傳回結果,它是一個Cold Observable。

Observable<R> ohValue = command.observe(); //hot observable

Observable<R> ocValue = command.toObservable(); //cold observable

在Hystrix的底層實作中大量使用了RxJava,這裡簡單介紹一下RxJava的觀察者-訂閱者模式。

Observable對象可以了解為事件源或者被觀察者,與之對應的是Subscriber對象,可以了解為訂閱者或者觀察者。

Observable用來向訂閱者Subscriber對象釋出事件,Subscriber對象則在接收到事件後對其進行處理,這裡所指的事件就是對依賴服務的調用。

一個Observable對象可以發送多個事件,直到結束或是發生異常。

Observable對象每發出一個事件,就會調用對應觀察者Subscriber對象的onNext()方法。

每一個Observable的執行,最後一定會通過調用Subscriber.onCompleted()或者Subscriber.onError()來結束該事件的操作流。

下面是一個簡單的例子

在該示例中,建立了一個簡單的事件源observable,一個對事件傳遞内容輸出的訂閱者subscriber,通過observable.subscribe(subscriber)來觸發事件的釋出。

關于Hot Observable和Cold Observable,其中Hot Observable不論事件源是否有訂閱者都會在建立後對事件進行釋出,是以對于Hot Observable的每一個訂閱者都有可能是從事件源的中途開始的,并且隻能看到整個操作的局部過程;而Cold Observable在沒有訂閱者的時候不會釋出事件,而是進行等待,直到有訂閱者之後才釋出事件,是以對于Cold Observable的訂閱者,它可以保證從一開始看到整個操作的全部過程。

實際上不止observe()和toObservable()使用了RxJava,execute()和queue()也都使用RxJava來實作。

execute()是通過queue()傳回的異步對象Future<R>的get()方法來實作同步執行的。該方法會等待任務執行結束,然後獲得R類型的結果進行傳回。

queue()則是通過toObservable()來獲得一個Cold Observable,并且通過toBlocking()将該Observable轉換成BlockingObservable,它可以把資料以阻塞的方式發射出來。而toFuture方法則是把BlockingObservable轉換成Future,該方法隻建立一個Future傳回,并不會阻塞,這使得消費者可以自己決定如何處理異步操作。而execute()就是直接使用了queue()傳回的Future中的阻塞方法get()來實作同步操作的。同時通過這種方式轉換的Future要求Observable隻發射一個資料,是以這兩個實作都隻能傳回單一結果。

3.結果是否被緩存

若目前指令的請求緩存功能是被啟用的,并且該指令緩存命中,那麼緩存的結果會立即以Observable對象的形式傳回。

4.斷路器是否打開

在指令結果沒有被緩存命中的時候,Hystrix會在執行指令前檢查斷路器是否為打開狀态

如果斷路器是打開的,那麼Hystrix不會執行指令,而是轉接到fallback處理邏輯(第8步)。

如果斷路器是關閉的,Hystrix會檢查是否有可用資源來執行指令(第5步)。

5.線程池、請求隊列、信号量是否占滿

如果與指令相關的線程池和請求隊列或者信号量(不使用線程池的時候)已經被占滿,那麼Hystrix不會執行指令,而是轉接到fallback處理邏輯(第8步)。注意,這個線程池不是容器的線程池而是Hystrix為了保證不會因為某個依賴服務的問題影響到其他依賴服務而采用了“艙壁模式”來隔離每個依賴的服務。

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

Hystrix會根據我們編寫的方法來決定采取什麼方式去請求依賴服務。

HystrixCommand.run()——傳回單個響應或抛出異常。

HystrixObservableCommand.construct()——傳回 Observable 對象來發射多個結果,或通過onError發送錯誤通知。

如果run()或construct()方法執行時長超過了指令的逾時閥值,其線程将抛出一個TimeoutException(或者在一個單獨的線程抛出,如果指令沒有運作在它自己的線程)。這種情況下 Hystrix轉接到fallback處理邏輯(第8步)。并且如果該指令沒有取消或中斷,它将放棄run()或construct()方法最終的傳回值。

如果指令沒有抛出異常并且傳回了響應,Hystrix 将會在執行一些日志記錄和度量報告之後傳回結果給調用者。如果是通過run()運作,Hystrix 将傳回 Observable 發射單個結果,然後發送一個onCompleted的通知;如果是通過construct()運作,Hystrix 直接傳回該方法産生的Observable對象。

7.計算斷路器的健康度

Hystrix會将成功、失敗、拒絕、逾時等資訊報告給斷路器,而斷路器會維護一組計數器來統計這些資料。斷路器會使用這些資料确定是否将斷路器打開,來對某個依賴服務的請求進行熔斷、短路,直到恢複期結束,若恢複期結束後,根據統計資料判斷仍未達到健康名額,會再次熔斷、短路。

8.fallback處理

當指令執行失敗時,Hystrix會進入fallback嘗試回退處理,也叫服務降級,可以引入服務降級的請求有下面幾種:

第4步,目前指令處于熔斷、短路狀态,斷路器是打開的時候。

第5步,目前指令的線程池、請求隊列、信号量被占滿的時候。

第6步,HystrixObservableCommand.construct()或HystrixCommand.run()抛出異常的時候。

在服務降級邏輯中,需要實作一個通用的響應結果,并且該結果的處理邏輯應當是從緩存或是根據一些靜态邏輯來擷取,而不是依賴網絡請求擷取,如果一定要在服務降級邏輯中包含網絡請求,那麼該請求也必須包裝在HystrixCommand或HystrixObservableCommand中,進而形成級聯的降級政策,而最終的降級邏輯一定不是一個依賴網絡請求的處理,而是一個能夠穩定的傳回結果的處理邏輯。

在 HystrixCommand 中,在 HystrixCommand.getFallback()方法中提供自定義的回調邏輯,方法傳回單個回調值。

在 HystrixObservableCommand 中,在HystrixObservableCommand.resumeWithFallback() 方法中提供自定義的回調邏輯,方法傳回一個Observable對象來發射一個或多個降級結果。

當指令的降級邏輯傳回結果後,Hystrix就将該結果傳回給調用者。當使用HystrixCommand.getFallback()的時候,它會傳回一個Observable對象,該對象會發射getFallback()的處理結果。而使用HystrixObservableCommand.resumeWithFallback()實作的時候會将Observable對象直接傳回。

如果沒有為指令實作降級邏輯或者降級邏輯中抛出了異常,Hystrix依然會傳回一個Observable對象,但是它不會發射任何結果資料,而是通過onError方法通知指令立即中斷請求,并通過onError()方法将異常發送給調用者,我們應該盡可能避免降級出現失敗的情況。如果降級政策邏輯執行發生失敗,Hystrix會根據不同的執行方式做出以下處理:

execute()——抛出異常

queue()——成功時傳回java.util.concurrent.Future,但如果調用 Future.get()将抛出異常

observe()——傳回 Observable 對象,當你訂閱該 Observable 時,将會立即終止并且調用訂閱者的onError方法

toObservable()——同observe()

9.傳回成功的響應

當Hystrix指令執行成功後,會将處理結果直接傳回或是以Observable的形式傳回,具體怎麼傳回要根據執行指令的方式來區分,下圖是四種執行方式的依賴關系:

springcloud(十):Hystrix工作流程分析

execute():和queue()擷取的方式一樣擷取一個 Future,然後通過調用get()方法阻塞并等待結果的傳回。

queue():将 toObservable()産生的原始Observable通過toBlocking()方法轉換成BlockingObservable對象,并調用它的toFuture()方法傳回異步的Future對象。

observe():在toObservable()産生原始Observable之後立即訂閱它,讓指令能夠馬上開始異步執行,并傳回一個Observable對象,當調用它的subscribe時,将重新産生結果和通知給訂閱者。

toObservable():傳回最原始的Observable,使用者必須訂閱它才能真正開始執行指令的訂閱流程。

---------------------

上一篇: Hello World
下一篇: yolov3