hystrix實(shí)現(xiàn)原理

流程圖

下圖展示了當(dāng)你使用 Hystrix 來(lái)包裝你請(qǐng)求依賴服務(wù)時(shí)的流程:

hystrix

接下來(lái)將詳細(xì)介紹如下問(wèn)題:

  • 1.構(gòu)建HystrixCommand或者HystrixObservableCommand對(duì)象
  • 2.執(zhí)行命令(即上述 Command 對(duì)象包裝的邏輯)
  • 3.結(jié)果是否有緩存
  • 4.請(qǐng)求線路(類似電路)是否是開(kāi)路
  • 5.線程池/請(qǐng)求隊(duì)列/信號(hào)量占滿時(shí)會(huì)發(fā)生什么
  • 6.使用HystrixObservableCommand.construct()還是HystrixCommand.run()
  • 7.計(jì)算鏈路健康度
  • 8.失敗回退邏輯
  • 9.返回正常回應(yīng)

1. 構(gòu)建HystrixCommand或者HystrixObservableCommand對(duì)象

使用 Hystrix 的第一步是創(chuàng)建一個(gè)HystrixCommand或者HystrixObservableCommand對(duì)象來(lái)表示你需要發(fā)給依賴服務(wù)的請(qǐng)求。你可以向構(gòu)造器傳遞任意參數(shù)。

若只期望依賴服務(wù)每次返回單一的回應(yīng)顷歌,按如下方式構(gòu)造一個(gè)HystrixCommand即可:

HystrixCommand command = new HystrixCommand(arg1, arg2);

若期望依賴服務(wù)返回一個(gè) Observable延塑,并應(yīng)用『Observer』模式監(jiān)聽(tīng)依賴服務(wù)的回應(yīng)胸懈,可按如下方式構(gòu)造一個(gè)HystrixObservableCommand

HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);

2. 執(zhí)行命令

Hystrix 命令提供四種方式(HystrixCommand支持所有四種方式,而HystrixObservableCommand僅支持后兩種方式)來(lái)執(zhí)行你包裝的請(qǐng)求:

  • execute()—— 阻塞旬陡,當(dāng)依賴服務(wù)響應(yīng)(或者拋出異常/超時(shí))時(shí)抬闯,返回結(jié)果

  • queue()—— 返回Future對(duì)象旧烧,通過(guò)該對(duì)象異步得到返回結(jié)果

  • observe()—— 返回Observable對(duì)象,立即發(fā)出請(qǐng)求画髓,在依賴服務(wù)響應(yīng)(或者拋出異常/超時(shí))時(shí),通過(guò)注冊(cè)的Subscriber得到返回結(jié)果

  • toObservable()—— 返回Observable對(duì)象平委,但只有在訂閱該對(duì)象時(shí)奈虾,才會(huì)發(fā)出請(qǐng)求,然后在依賴服務(wù)響應(yīng)(或者拋出異常/超時(shí))時(shí)廉赔,通過(guò)注冊(cè)的Subscriber得到返回結(jié)果

K             value   = command.execute();
Future<K>     fValue  = command.queue();
Observable<K> ohValue = command.observe();         // hot observable(注:調(diào)用observe()方法時(shí)肉微,請(qǐng)求立即發(fā)出)
Observable<K> ocValue = command.toObservable();    // cold observable(注:只有在返回的ocValue上調(diào)用subscribe時(shí),才會(huì)發(fā)出請(qǐng)求)

在內(nèi)部實(shí)現(xiàn)中蜡塌,execute()是同步調(diào)用碉纳,內(nèi)部會(huì)調(diào)用queue().get()方法。queue()內(nèi)部會(huì)調(diào)用toObservable().toBlocking().toFuture()馏艾。也就是說(shuō)劳曹,HystrixCommand內(nèi)部均通過(guò)一個(gè)Observable的實(shí)現(xiàn)來(lái)執(zhí)行請(qǐng)求,即使這些命令本來(lái)是用來(lái)執(zhí)行同步返回回應(yīng)這樣的簡(jiǎn)單邏輯琅摩。

3. 結(jié)果是否有緩存

如果請(qǐng)求結(jié)果緩存這個(gè)特性被啟用铁孵,并且緩存命中,則緩存的回應(yīng)會(huì)立即通過(guò)一個(gè)Observable對(duì)象的形式返回房资。

4. 請(qǐng)求線路是否是開(kāi)路

當(dāng)執(zhí)行一個(gè)命令時(shí)蜕劝,Hystrix 會(huì)先檢查熔斷器狀態(tài),確定請(qǐng)求線路是否是開(kāi)路

如果請(qǐng)求線路是開(kāi)路,Hystrix 將不會(huì)執(zhí)行這個(gè)命令岖沛,而是直接使用『失敗回退邏輯』

5. 線程池/請(qǐng)求隊(duì)列/信號(hào)量占滿時(shí)會(huì)發(fā)生什么

如果和當(dāng)前需要執(zhí)行的命令相關(guān)聯(lián)的線程池和請(qǐng)求隊(duì)列(或者信號(hào)量暑始,如果不使用線程池),Hystrix 將不會(huì)執(zhí)行這個(gè)命令婴削,而是直接使用『失敗回退邏輯』

6. 使用HystrixObservableCommand.construct()還是HystrixCommand.run()

Hystrix 將根據(jù)你使用類的不同廊镜,內(nèi)部使用不同的方式來(lái)請(qǐng)求依賴服務(wù):

  • HystrixCommand.run()—— 返回回應(yīng)或者拋出異常

  • HystrixObservableCommand.construct()—— 返回 Observable 對(duì)象,并在回應(yīng)到達(dá)時(shí)通知 observers馆蠕,或者回調(diào)onError方法通知出現(xiàn)異常

run()或者construct()方法耗時(shí)超過(guò)了給命令設(shè)置的超時(shí)閾值期升,執(zhí)行請(qǐng)求的線程將拋出TimeoutException(若命令本身并不在其調(diào)用線程內(nèi)執(zhí)行,則單獨(dú)的定時(shí)器線程會(huì)拋出該異常)互躬。在這種情況下播赁,Hystrix 將會(huì)執(zhí)行失敗回退邏輯,并且會(huì)忽略最終(若執(zhí)行命令的線程沒(méi)有被中斷)返回的回應(yīng)吼渡。

若命令本身并不拋出異常容为,并正常返回回應(yīng),Hystrix 在添加一些日志和監(jiān)控?cái)?shù)據(jù)采集之后寺酪,直接返回回應(yīng)坎背。Hystrix 在使用run()方法時(shí),Hystrix 內(nèi)部還是會(huì)生成一個(gè)Observable對(duì)象寄雀,并返回單個(gè)請(qǐng)求得滤,產(chǎn)生一個(gè)onCompleted通知;而在 Hystrix 使用construct()時(shí)盒犹,會(huì)直接返回由construct()產(chǎn)生的Observable對(duì)象

7. 計(jì)算線路健康度

Hystrix 會(huì)將請(qǐng)求成功懂更,失敗,被拒絕或超時(shí)信息報(bào)告給熔斷器急膀,熔斷器維護(hù)一些用于統(tǒng)計(jì)數(shù)據(jù)用的計(jì)數(shù)器沮协。

這些計(jì)數(shù)器產(chǎn)生的統(tǒng)計(jì)數(shù)據(jù)使得熔斷器在特定的時(shí)刻,能短路某個(gè)依賴服務(wù)的后續(xù)請(qǐng)求卓嫂,直到恢復(fù)期結(jié)束慷暂,若恢復(fù)期結(jié)束根據(jù)統(tǒng)計(jì)數(shù)據(jù)熔斷器判定線路仍然未恢復(fù)健康,熔斷器會(huì)再次關(guān)閉線路晨雳。

8. 失敗回退邏輯

當(dāng)命令執(zhí)行失敗時(shí)行瑞,Hystrix 將會(huì)執(zhí)行失敗回退邏輯,失敗原因可能是:

  • construct()run()方法拋出異常
  • 當(dāng)線路是開(kāi)路悍募,導(dǎo)致命令被短路時(shí)
  • 當(dāng)命令對(duì)應(yīng)的線程池或信號(hào)量被占滿
  • 超時(shí)

失敗回退邏輯包含了通用的回應(yīng)信息蘑辑,這些回應(yīng)從內(nèi)存緩存中或者其他固定邏輯中得到,而不應(yīng)有任何的網(wǎng)絡(luò)依賴坠宴。如果一定要在失敗回退邏輯中包含網(wǎng)絡(luò)請(qǐng)求洋魂,必須將這些網(wǎng)絡(luò)請(qǐng)求包裝在另一個(gè)HystrixCommandHystrixObservableCommand中。

當(dāng)使用HystrixCommand時(shí),通過(guò)實(shí)現(xiàn)HystrixCommand.getFallback()返回失敗回退時(shí)的回應(yīng)副砍。

當(dāng)使用HystrixObservableCommand時(shí)衔肢,通過(guò)實(shí)現(xiàn)HystrixObservableCommand.resumeWithFallback()返回 Observable 對(duì)象來(lái)通知 observers 失敗回退時(shí)的回應(yīng)。

若失敗回退方法返回回應(yīng)豁翎,Hystrix 會(huì)將這個(gè)回應(yīng)返回給命令的調(diào)用者角骤。若 Hystrix 內(nèi)部調(diào)用HystrixCommand.getFallback()時(shí),會(huì)產(chǎn)生一個(gè) Observable 對(duì)象心剥,并包裝用戶實(shí)現(xiàn)的getFallback()方法返回的回應(yīng)邦尊;若 Hystrix 內(nèi)部調(diào)用HystrixObservableCommand.resumeWithFallback()時(shí),會(huì)將用戶實(shí)現(xiàn)的resumeWithFallback()返回的 Observable 對(duì)象直接返回优烧。

若你沒(méi)有實(shí)現(xiàn)失敗回退方法蝉揍,或者失敗回退方法拋出異常,Hystrix 內(nèi)部還是會(huì)生成一個(gè) Observable 對(duì)象畦娄,但它不會(huì)產(chǎn)生任何回應(yīng)又沾,并通過(guò)onError通知立即中止請(qǐng)求。Hystrix 默認(rèn)會(huì)通過(guò)onError通知調(diào)用者發(fā)生了何種異常熙卡。你需要盡量避免失敗回退方法執(zhí)行失敗杖刷,保持該方法盡可能的簡(jiǎn)單不易出錯(cuò)。

若失敗回退方法執(zhí)行失敗驳癌,或者用戶未提供失敗回退方法滑燃,Hystrix 會(huì)根據(jù)調(diào)用執(zhí)行命令的方法的不同而產(chǎn)生不同的行為:

  • execute()—— 拋出異常

  • queue()—— 成功返回Future對(duì)象,但其get()方法被調(diào)用時(shí)颓鲜,會(huì)拋出異常

  • observe()—— 返回Observable對(duì)象不瓶,當(dāng)你訂閱它的時(shí)候,會(huì)立即調(diào)用 subscriber 的onError方法中止請(qǐng)求

  • toObservable()—— 返回Observable對(duì)象灾杰,當(dāng)你訂閱它的時(shí)候,會(huì)立即調(diào)用 subscriber 的onError方法中止請(qǐng)求

9. 返回正澄醪危回應(yīng)

若命令成功被執(zhí)行艳吠,Hystrix 將回應(yīng)返回給調(diào)用方,或者通過(guò)Observable的形式返回孽椰。根據(jù)上述調(diào)用命令方式的不同(如第2條所示)昭娩,Observable對(duì)象會(huì)進(jìn)行一些轉(zhuǎn)換:

Observable對(duì)象的轉(zhuǎn)化
  • execute()—— 產(chǎn)生一個(gè)Future對(duì)象,行為同.queue()產(chǎn)生的Future對(duì)象一樣黍匾,接著調(diào)用其get()方法栏渺,生成由內(nèi)部產(chǎn)生的Observable對(duì)象返回的回應(yīng)

  • queue()—— 將內(nèi)部產(chǎn)生的Observable對(duì)象轉(zhuǎn)換(Decorator模式)成BlockingObservable對(duì)象,以產(chǎn)生并返回Future對(duì)象

  • observe()—— 產(chǎn)生Observable對(duì)象后锐涯,立即訂閱(ReplaySubject)以使命令得以執(zhí)行(異步)磕诊,返回該Observable對(duì)象,當(dāng)你調(diào)用其subscribe方法時(shí),重放產(chǎn)生的回應(yīng)信息和通知給用戶提供的訂閱者

  • toObservable()—— 返回Observable對(duì)象霎终,你必須調(diào)用其subscribe方法滞磺,以使命令得以執(zhí)行。

熔斷器

下圖展示了HystrixCommandHystrixObservableCommand如何與HystrixCircuitBreaker進(jìn)行交互莱褒,以及HystrixCircuitBreaker的決策邏輯流程击困,包括熔斷器內(nèi)部計(jì)數(shù)器如何工作。

熔斷器執(zhí)行邏輯

線路的開(kāi)路閉路詳細(xì)邏輯如下:

  • 1.假設(shè)線路內(nèi)的容量(請(qǐng)求QPS)達(dá)到一定閾值(通過(guò)HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()配置)
  • 2.同時(shí)广凸,假設(shè)線路內(nèi)的錯(cuò)誤率達(dá)到一定閾值(通過(guò)HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()配置)
  • 3.熔斷器將從『閉路』轉(zhuǎn)換成『開(kāi)路』
  • 4.若此時(shí)是『開(kāi)路』狀態(tài)阅茶,熔斷器將短路后續(xù)所有經(jīng)過(guò)該熔斷器的請(qǐng)求,這些請(qǐng)求直接走『失敗回退邏輯』
  • 5.經(jīng)過(guò)一定時(shí)間(即『休眠窗口』谅海,通過(guò)HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()配置)脸哀,后續(xù)第一個(gè)請(qǐng)求將會(huì)被允許通過(guò)熔斷器(此時(shí)熔斷器處于『半開(kāi)』狀態(tài)),若該請(qǐng)求失敗胁赢,熔斷器將又進(jìn)入『開(kāi)路』狀態(tài)企蹭,且在休眠窗口內(nèi)保持此狀態(tài);若該請(qǐng)求成功智末,熔斷器將進(jìn)入『閉路』狀態(tài)谅摄,回到邏輯1循環(huán)往復(fù)。

依賴隔離

Hystrix 通過(guò)使用『艙壁模式』(注:將船的底部劃分成一個(gè)個(gè)的艙室系馆,這樣一個(gè)艙室進(jìn)水不會(huì)導(dǎo)致整艘船沉沒(méi)送漠。將系統(tǒng)所有依賴服務(wù)隔離起來(lái),一個(gè)依賴延遲升高或者失敗由蘑,不會(huì)導(dǎo)致整個(gè)系統(tǒng)失斆龉选)來(lái)隔離依賴服務(wù),并限制訪問(wèn)這些依賴服務(wù)的并發(fā)度尼酿。

依賴隔離

線程&線程池

通過(guò)將對(duì)依賴服務(wù)的訪問(wèn)執(zhí)行放到單獨(dú)的線程爷狈,將其與調(diào)用線程(例如 Tomcat 線程池中的線程)隔離開(kāi)來(lái),調(diào)用線程能空出來(lái)去做其他的工作而不至于被依賴服務(wù)的訪問(wèn)阻塞過(guò)長(zhǎng)時(shí)間裳擎。

Hystrix 使用獨(dú)立的涎永,每個(gè)依賴服務(wù)對(duì)應(yīng)一個(gè)線程池的方式,來(lái)隔離這些依賴服務(wù)鹿响,這樣羡微,某個(gè)依賴服務(wù)的高延遲只會(huì)拖慢這個(gè)依賴服務(wù)對(duì)應(yīng)的線程池。

高延遲請(qǐng)求的例子

當(dāng)然惶我,也可以不使用線程池來(lái)使你的系統(tǒng)免受依賴服務(wù)失效的影響妈倔,這需要你小心的設(shè)置網(wǎng)絡(luò)連接/讀取超時(shí)時(shí)間和重試配置,并保證這些配置能正確正常的運(yùn)作绸贡,以使這些依賴服務(wù)在失效時(shí)盯蝴,能快速返回錯(cuò)誤毅哗。

Netflix 在設(shè)計(jì) Hystrix 時(shí),使用線程/線程池來(lái)實(shí)現(xiàn)隔離结洼,原因如下:

  • 多數(shù)系統(tǒng)同時(shí)運(yùn)行了(有時(shí)甚至多達(dá)數(shù)百個(gè))不同的后端服務(wù)黎做,這些服務(wù)由不同開(kāi)發(fā)組開(kāi)發(fā)。

  • 每個(gè)服務(wù)都提供了自己的客戶端庫(kù)

  • 客戶端庫(kù)經(jīng)常會(huì)發(fā)生變動(dòng)

  • 客戶端庫(kù)可能會(huì)改變邏輯松忍,加入新的網(wǎng)絡(luò)請(qǐng)求

  • 客戶端庫(kù)可能會(huì)包含重試邏輯蒸殿,數(shù)據(jù)解析,緩存(本地緩存或分布式緩存)鸣峭,或者其他類似邏輯

  • 客戶端庫(kù)對(duì)于使用者來(lái)說(shuō)宏所,相當(dāng)于『黑盒』,其實(shí)現(xiàn)細(xì)節(jié)摊溶,網(wǎng)絡(luò)訪問(wèn)方式爬骤,默認(rèn)配置等等均對(duì)使用者透明

  • In several real-world production outages the determination was “oh, something changed and properties should be adjusted” or “the client library changed its behavior.”

  • 即使客戶端庫(kù)本身未發(fā)生變化,服務(wù)自身發(fā)生變化莫换,也可能會(huì)影響其性能霞玄,從而導(dǎo)致客戶端配置不再可靠

  • 中間依賴服務(wù)可能包含一些其依賴服務(wù)提供的客戶端庫(kù),而這些庫(kù)可能不受控且配置不合理

  • 絕大多數(shù)網(wǎng)絡(luò)訪問(wèn)都采用同步的方式進(jìn)行

  • 客戶端代碼可能也會(huì)有失效或者高延遲拉岁,而不僅僅是在網(wǎng)絡(luò)訪問(wèn)時(shí)

面對(duì)失效時(shí) Hystrix 包裝的請(qǐng)求拓?fù)鋱D
線程池的優(yōu)勢(shì)

將依賴服務(wù)請(qǐng)求通過(guò)使用不同的線程池隔離坷剧,其優(yōu)勢(shì)如下:

  • 系統(tǒng)完全與依賴服務(wù)請(qǐng)求隔離開(kāi)來(lái),即使依賴服務(wù)對(duì)應(yīng)線程池耗盡喊暖,也不會(huì)影響系統(tǒng)其它請(qǐng)求

  • 降低了系統(tǒng)接入新的依賴服務(wù)的風(fēng)險(xiǎn)惫企,若新的依賴服務(wù)存在問(wèn)題,也不會(huì)影響系統(tǒng)其它請(qǐng)求

  • 當(dāng)依賴服務(wù)失效后又恢復(fù)正常陵叽,其對(duì)應(yīng)的線程池會(huì)被清理干凈狞尔,相對(duì)于整個(gè) Tomcat 容器的線程池被占滿需要耗費(fèi)更長(zhǎng)時(shí)間以恢復(fù)可用來(lái)說(shuō),此時(shí)系統(tǒng)可以快速恢復(fù)

  • 若依賴服務(wù)的配置有問(wèn)題巩掺,線程池能迅速反映出來(lái)(通過(guò)失敗次數(shù)的增加偏序,高延遲,超時(shí)胖替,拒絕訪問(wèn)等等)禽车,同時(shí),你可以在不影響系統(tǒng)現(xiàn)有功能的情況下刊殉,處理這些問(wèn)題(通常通過(guò)熱配置等方式)

  • 若依賴服務(wù)的實(shí)現(xiàn)發(fā)生變更,性能有了很大的變化(這種情況時(shí)常發(fā)生)州胳,需要進(jìn)行配置調(diào)整(例如增加/減小超時(shí)閾值记焊,調(diào)整重試策略等)時(shí),也可以從線程池的監(jiān)控信息上迅速反映出來(lái)(失敗次數(shù)增加栓撞,高延遲遍膜,超時(shí)碗硬,拒絕訪問(wèn)等等),同時(shí)瓢颅,你可以在不影響其他依賴服務(wù)恩尾,系統(tǒng)請(qǐng)求和用戶的情況下,處理這些問(wèn)題

  • 線程池處理能起到隔離的作用以外挽懦,還能通過(guò)這種內(nèi)置的并發(fā)特性翰意,在客戶端庫(kù)同步網(wǎng)絡(luò)IO上,建立一個(gè)異步的 Facade(類似 Netflix API 建立在 Hystrix 命令上的 Reactive信柿、全異步化的那一套 Java API)

簡(jiǎn)而言之冀偶,通過(guò)線程池提供的依賴服務(wù)隔離,可以使得我們能在不停止服務(wù)的情況下渔嚷,更加優(yōu)雅地應(yīng)對(duì)客戶端庫(kù)和子系統(tǒng)性能上的變化进鸠。

注:盡管線程池能提供隔離性,但你仍然需要對(duì)你的依賴服務(wù)客戶端代碼增加超時(shí)邏輯形病,并且/或者處理線程中斷異常客年,以使這些代碼不會(huì)無(wú)故地阻塞或者拖慢 Hystrix 線程池。

線程池的弊端

使用線程池的主要弊端是會(huì)增加系統(tǒng) CPU 的負(fù)載漠吻,每個(gè)命令的執(zhí)行量瓜,都包含了 CPU 任務(wù)的排隊(duì),調(diào)度侥猩,上下文切換榔至。

Netflix 在設(shè)計(jì) Hystrix 時(shí),認(rèn)為相對(duì)于其帶來(lái)的好處欺劳,其帶來(lái)的負(fù)載的一點(diǎn)點(diǎn)升高對(duì)系統(tǒng)的影響是微乎其微的唧取。

線程池的開(kāi)銷

Hystrix 的開(kāi)發(fā)人員測(cè)試了在子線程中執(zhí)行construct()run()方法帶來(lái)的額外時(shí)延,以及在父線程中整個(gè)請(qǐng)求的耗時(shí)划提,通過(guò)這個(gè)測(cè)試枫弟,你能直觀了解 Hystrix 使用線程池帶來(lái)的一點(diǎn)點(diǎn)系統(tǒng)負(fù)載的升高影響(線程,監(jiān)控鹏往,日志淡诗,熔斷器等)。

Netflix API 使用線程池來(lái)隔離依賴服務(wù)伊履,每天可以處理超過(guò) 100 億的 Hystrix 命令韩容,每個(gè) API 實(shí)例有超過(guò) 40 個(gè)線程池,每個(gè)線程池有 5 到 20 個(gè)工作線程(絕大部分設(shè)置為 10 個(gè)線程)唐瀑。

下圖展示了一個(gè)HystrixCommand以 60QPS 的速度群凶,在一個(gè) API 實(shí)例(每臺(tái)服務(wù)器每秒運(yùn)行的線程數(shù)峰值為 350)上被執(zhí)行的耗時(shí)監(jiān)控:

線程池開(kāi)銷

(注:有 User 的表示使用線程池來(lái)隔離依賴服務(wù)后的耗時(shí))

中位數(shù)顯示二者(未使用線程池和使用線程池)沒(méi)有差別。

90% 的情況下哄辣,使用線程池有 3ms 的延遲

99% 的情況下请梢,使用線程池有 9ms 的延遲赠尾,盡管如此,相對(duì)于請(qǐng)求的總時(shí)間(2ms28ms)毅弧,延遲(0ms9ms)基本可以忽略不計(jì)

90% 的情況下气嫁,這些延遲和在使用了熔斷器之后更高的延遲,在絕大多數(shù) Netflix 的需求來(lái)看够坐,是微不足道的寸宵,更何況其能帶來(lái)系統(tǒng)穩(wěn)定性和魯棒性上的巨大提升。

對(duì)于那些本來(lái)延遲就比較小的請(qǐng)求(例如訪問(wèn)本地緩存成功率很高的請(qǐng)求)來(lái)說(shuō)咆霜,線程池帶來(lái)的開(kāi)銷是非常高的邓馒,這時(shí),你可以考慮采用其他方法蛾坯,例如非阻塞信號(hào)量(不支持超時(shí))光酣,來(lái)實(shí)現(xiàn)依賴服務(wù)的隔離,使用信號(hào)量的開(kāi)銷很小脉课。但絕大多數(shù)情況下救军,Netflix 更偏向于使用線程池來(lái)隔離依賴服務(wù),因?yàn)槠鋷?lái)的額外開(kāi)銷可以接受倘零,并且能支持包括超時(shí)在內(nèi)的所有功能唱遭。

信號(hào)量

除了線程池,隊(duì)列之外呈驶,你可以使用信號(hào)量(或者叫計(jì)數(shù)器)來(lái)限制單個(gè)依賴服務(wù)的并發(fā)度拷泽。Hystrix 可以利用信號(hào)量,而不是線程池袖瞻,來(lái)控制系統(tǒng)負(fù)載司致,但信號(hào)量不允許我們?cè)O(shè)置超時(shí)和異步化,如果你對(duì)客戶端庫(kù)有足夠的信任(延遲不會(huì)過(guò)高)聋迎,并且你只需要控制系統(tǒng)負(fù)載脂矫,那么你可以使用信號(hào)量。

HystrixCommandHystrixObservableCommand在兩個(gè)地方支持使用信號(hào)量:

  • 失敗回退邏輯:當(dāng) Hystrix 需要執(zhí)行失敗回退邏輯時(shí)霉晕,其在調(diào)用線程(Tomcat 線程)中使用信號(hào)量

  • 執(zhí)行命令時(shí):如果設(shè)置了 Hystrix 命令的execution.isolation.strategy屬性為SEMAPHORE庭再,則 Hystrix 會(huì)使用信號(hào)量而不是線程池來(lái)控制調(diào)用線程調(diào)用依賴服務(wù)的并發(fā)度

你可以通過(guò)動(dòng)態(tài)配置(即熱部署)來(lái)決定信號(hào)量的大小,以控制并發(fā)線程的數(shù)量牺堰,信號(hào)量大小的估計(jì)和使用線程池進(jìn)行并發(fā)度估計(jì)一樣(僅訪問(wèn)內(nèi)存數(shù)據(jù)的請(qǐng)求拄轻,一般能達(dá)到耗時(shí)在 1ms 以內(nèi),且能達(dá)到 5000rps伟葫,這樣的請(qǐng)求對(duì)應(yīng)的信號(hào)量可以設(shè)置為 1 或者 2恨搓。默認(rèn)值為 10)。

注意:如果依賴服務(wù)使用信號(hào)量來(lái)進(jìn)行隔離扒俯,當(dāng)依賴服務(wù)出現(xiàn)高延遲奶卓,其調(diào)用線程也會(huì)被阻塞,直到依賴服務(wù)的網(wǎng)絡(luò)請(qǐng)求超時(shí)撼玄。

信號(hào)量在達(dá)到上限時(shí)夺姑,會(huì)拒絕后續(xù)請(qǐng)求的訪問(wèn),同時(shí)掌猛,設(shè)置信號(hào)量的線程也無(wú)法異步化(即像線程池那樣盏浙,實(shí)現(xiàn)『提交-做其他工作-得到結(jié)果』模式)

請(qǐng)求合并

你可以在HystrixCommand之前放置一個(gè)『請(qǐng)求合并器』(HystrixCollapser為請(qǐng)求合并器的抽象父類),該合并器可以將多個(gè)發(fā)往同一個(gè)后端依賴服務(wù)的請(qǐng)求合并成一個(gè)荔茬。

下圖展示了在兩種場(chǎng)景(未增加『請(qǐng)求合并器』和增加『請(qǐng)求合并器』)下废膘,線程和網(wǎng)絡(luò)連接數(shù)量(假設(shè)所有請(qǐng)求在一個(gè)很小的時(shí)間窗口內(nèi),例如 10ms慕蔚,是『并發(fā)』的):

請(qǐng)求合并

為什么要使用請(qǐng)求合并丐黄?

在并發(fā)執(zhí)行HystrixCommand時(shí),利用請(qǐng)求合并能減少線程和網(wǎng)絡(luò)連接數(shù)量孔飒。通過(guò)使用HystrixCollapser灌闺,Hystrix 能自動(dòng)完成請(qǐng)求的合并,開(kāi)發(fā)者不需要對(duì)現(xiàn)有代碼做批量化的開(kāi)發(fā)坏瞄。

全局上下文(適用于所有 Tomcat 線程)

理想情況下桂对,合并過(guò)程應(yīng)該發(fā)生在系統(tǒng)全局層面,這樣用戶發(fā)起的鸠匀,由 Tomcat 線程執(zhí)行的所有請(qǐng)求都能被執(zhí)行合并操作蕉斜。

例如,有這樣一個(gè)需求缀棍,用戶需要獲取電影評(píng)級(jí)宅此,而這些數(shù)據(jù)需要系統(tǒng)請(qǐng)求依賴服務(wù)來(lái)獲取,對(duì)依賴服務(wù)的請(qǐng)求使用HystrixCommand進(jìn)行包裝睦柴,并增加了請(qǐng)求合并的配置诽凌,這樣,當(dāng)同一個(gè) JVM 中其他線程需要執(zhí)行同樣的請(qǐng)求時(shí)坦敌,Hystrix 會(huì)將這個(gè)請(qǐng)求同其他同樣的請(qǐng)求合并侣诵,只產(chǎn)生一個(gè)網(wǎng)絡(luò)請(qǐng)求。

注意:合并器會(huì)傳遞一個(gè)HystrixRequestContext對(duì)象到合并的網(wǎng)絡(luò)請(qǐng)求中狱窘,因此杜顺,下游系統(tǒng)需要支持批量化,以使請(qǐng)求合并發(fā)揮其高效的特點(diǎn)蘸炸。

用戶請(qǐng)求上下文(適用于單個(gè) Tomcat 線程)

如果給HystrixCommand只配置成針對(duì)單個(gè)用戶進(jìn)行請(qǐng)求合并躬络,則 Hystrix 只會(huì)在單個(gè) Tomcat 線程(即請(qǐng)求)中進(jìn)行請(qǐng)求合并。

例如搭儒,如果用戶想加載 300 個(gè)視頻對(duì)象的書簽穷当,請(qǐng)求合并后提茁,Hystrix 會(huì)將原本需要發(fā)起的 300 個(gè)網(wǎng)絡(luò)請(qǐng)求合并到一個(gè)。

對(duì)象模型和代碼復(fù)雜度

很多時(shí)候馁菜,當(dāng)你創(chuàng)建一個(gè)對(duì)象模型茴扁,適用于對(duì)象的消費(fèi)者邏輯,結(jié)果發(fā)現(xiàn)這個(gè)模型會(huì)導(dǎo)致生產(chǎn)者無(wú)法充分利用其擁有的資源汪疮。

例如峭火,這里有一個(gè)包含 300 個(gè)視頻對(duì)象的列表,需要遍歷這個(gè)列表智嚷,并對(duì)每一個(gè)對(duì)象調(diào)用getSomeAttribute()方法卖丸,這是一個(gè)顯而易見(jiàn)的對(duì)象模型,但如果簡(jiǎn)單處理的話盏道,可能會(huì)導(dǎo)致 300 次的網(wǎng)絡(luò)請(qǐng)求(假設(shè)getSomeAttribute()方法內(nèi)需要發(fā)出網(wǎng)絡(luò)請(qǐng)求)稍浆,每一個(gè)網(wǎng)絡(luò)請(qǐng)求可能都會(huì)花上幾毫秒(顯然,這種方式非常容易拖慢系統(tǒng))摇天。

當(dāng)然粹湃,你也可以要求用戶在調(diào)用getSomeAttribute()之前,先判斷一下哪些視頻對(duì)象真正需要請(qǐng)求其屬性泉坐。

或者为鳄,你可以將對(duì)象模型進(jìn)行拆分,從一個(gè)地方獲取視頻列表腕让,然后從另一個(gè)地方獲取視頻的屬性孤钦。

但這些實(shí)現(xiàn)會(huì)導(dǎo)致 API 非常丑陋,且實(shí)現(xiàn)的對(duì)象模型無(wú)法完全滿足用戶使用模式纯丸。 并且在企業(yè)級(jí)開(kāi)發(fā)時(shí)偏形,很容易因?yàn)殚_(kāi)發(fā)者的疏忽導(dǎo)致錯(cuò)誤或者不夠高效,因?yàn)椴煌拈_(kāi)發(fā)者可能有不同的請(qǐng)求方式觉鼻,這樣一個(gè)地方的優(yōu)化不足以保證在所有地方都會(huì)有優(yōu)化俊扭。

通過(guò)將合并邏輯下沉到 Hystrix 層,不管你如何設(shè)計(jì)對(duì)象模型坠陈,或者以何種方式去調(diào)用依賴服務(wù)萨惑,又或者開(kāi)發(fā)者是否意識(shí)到這些邏輯需要不需要進(jìn)行優(yōu)化,這些都不需要考慮仇矾,因?yàn)?Hystrix 能統(tǒng)一處理庸蔼。

getSomeAttribute()方法能放在它最適合的位置,并且能以最適合的方式被調(diào)用贮匕,Hystrix 的請(qǐng)求合并器會(huì)自動(dòng)將請(qǐng)求合并到合并時(shí)間窗口內(nèi)姐仅。

請(qǐng)求合并帶來(lái)的額外開(kāi)銷

請(qǐng)求合并會(huì)導(dǎo)致依賴服務(wù)的請(qǐng)求延遲增高(該延遲為等待請(qǐng)求的延遲),延遲的最大值為合并時(shí)間窗口大小。

若某個(gè)請(qǐng)求耗時(shí)的中位數(shù)是 5ms掏膏,合并時(shí)間窗口為 10ms劳翰,那么在最壞情況下(注:合并時(shí)間窗口開(kāi)啟時(shí)發(fā)起請(qǐng)求),請(qǐng)求需要消耗 15ms 才能完成馒疹。通常情況下磕道,請(qǐng)求不太可能恰好在合并時(shí)間窗口開(kāi)啟時(shí)發(fā)起,因此行冰,請(qǐng)求合并帶來(lái)的額外開(kāi)銷應(yīng)該是合并時(shí)間窗口的一般,在此例中是 5ms伶丐。

請(qǐng)求合并帶來(lái)的額外開(kāi)銷是否值得悼做,取決于將要執(zhí)行的命令,高延遲的命令相比較而言不會(huì)有太大的影響哗魂。同時(shí)肛走,緩存 Key 的選擇也決定了在一個(gè)合并時(shí)間窗口內(nèi)能『并發(fā)』執(zhí)行的命令數(shù)量:如果一個(gè)合并時(shí)間窗口內(nèi)只有 1~2 個(gè)請(qǐng)求,將請(qǐng)求合并顯然不是明智的選擇录别。事實(shí)上朽色,如果單線程循環(huán)調(diào)用同一個(gè)依賴服務(wù)的情況下,如果將請(qǐng)求合并组题,會(huì)導(dǎo)致這個(gè)循環(huán)成為系統(tǒng)性能的瓶頸葫男,因?yàn)槊恳粋€(gè)請(qǐng)求都需要等待 10ms 的合并時(shí)間周期。

然而崔列,如果一個(gè)命令具有高并發(fā)度梢褐,并且能批量處理多個(gè),甚至上百個(gè)的話赵讯,請(qǐng)求合并帶來(lái)的性能開(kāi)銷會(huì)因?yàn)橥掏铝康臉O大提升而基本可以忽略盈咳,因?yàn)?Hystrix 會(huì)減少這些請(qǐng)求所需的線程和網(wǎng)絡(luò)連接數(shù)量。

請(qǐng)求合并器的執(zhí)行流程
請(qǐng)求合并器的執(zhí)行流程

請(qǐng)求緩存

HystrixCommandHystrixObservableCommand的實(shí)現(xiàn)中边翼,你可以定義一個(gè)緩存的 Key鱼响,這個(gè) Key 用于在同一個(gè)請(qǐng)求上下文(全局或者用戶級(jí))中標(biāo)識(shí)緩存的請(qǐng)求結(jié)果,當(dāng)然组底,該緩存是線程安全的丈积。

下例展示了在一個(gè)完整 HTTP 請(qǐng)求周期內(nèi),兩個(gè)線程執(zhí)行命令的流程:

請(qǐng)求緩存例子

請(qǐng)求緩存有如下好處:

  • 不同請(qǐng)求路徑上針對(duì)同一個(gè)依賴服務(wù)進(jìn)行的重復(fù)請(qǐng)求(有同一個(gè)緩存 Key)斤寇,不會(huì)真實(shí)請(qǐng)求多次

這個(gè)特性在企業(yè)級(jí)系統(tǒng)中非常有用桶癣,在這些系統(tǒng)中,開(kāi)發(fā)者往往開(kāi)發(fā)的只是系統(tǒng)功能的一部分娘锁。(注:這樣牙寞,開(kāi)發(fā)者彼此隔離,不太可能使用同樣的方法或者策略去請(qǐng)求同一個(gè)依賴服務(wù)提供的資源)

例如,請(qǐng)求一個(gè)用戶的Account的邏輯如下所示间雀,這個(gè)邏輯往往在系統(tǒng)不同地方被用到:

Account account = new UserGetAccount(accountId).execute();
//or
Observable<Account> accountObservable = new UserGetAccount(accountId).observe();

Hystrix 的RequestCache只會(huì)在內(nèi)部執(zhí)行run()方法一次悔详,上面兩個(gè)線程在執(zhí)行HystrixCommand命令時(shí),會(huì)得到相同的結(jié)果惹挟,即使這兩個(gè)命令是兩個(gè)不同的實(shí)例茄螃。

  • 數(shù)據(jù)獲取具有一致性

因?yàn)榫彺娴拇嬖冢说谝淮握?qǐng)求需要真正訪問(wèn)依賴服務(wù)以外连锯,后續(xù)請(qǐng)求全部從緩存中獲取归苍,可以保證在同一個(gè)用戶請(qǐng)求內(nèi)挪略,不會(huì)出現(xiàn)依賴服務(wù)返回不同的回應(yīng)的情況笛粘。

  • 避免不必要的線程執(zhí)行

construct()run()方法執(zhí)行之前谍失,會(huì)先從請(qǐng)求緩存中獲取數(shù)據(jù)躺坟,因此睹栖,Hystrix 能利用這個(gè)特性避免不必要的線程執(zhí)行房蝉,減小系統(tǒng)開(kāi)銷玄柠。

若 Hystrix 沒(méi)有實(shí)現(xiàn)請(qǐng)求緩存融欧,那么HystrixCommandHystrixObservableCommand的實(shí)現(xiàn)者需要自己在construct()run()方法中實(shí)現(xiàn)緩存咏连,這種方式無(wú)法避免不必要的線程執(zhí)行開(kāi)銷盯孙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市祟滴,隨后出現(xiàn)的幾起案子振惰,更是在濱河造成了極大的恐慌,老刑警劉巖垄懂,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件报账,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡埠偿,警方通過(guò)查閱死者的電腦和手機(jī)透罢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)冠蒋,“玉大人羽圃,你說(shuō)我怎么就攤上這事《督耍” “怎么了朽寞?”我有些...
    開(kāi)封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)斩郎。 經(jīng)常有香客問(wèn)我脑融,道長(zhǎng),這世上最難降的妖魔是什么缩宜? 我笑而不...
    開(kāi)封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任肘迎,我火速辦了婚禮甥温,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妓布。我一直安慰自己姻蚓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布匣沼。 她就那樣靜靜地躺著狰挡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪释涛。 梳的紋絲不亂的頭發(fā)上加叁,一...
    開(kāi)封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音唇撬,去河邊找鬼殉农。 笑死,一個(gè)胖子當(dāng)著我的面吹牛局荚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播愈污,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼耀态,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了暂雹?” 一聲冷哼從身側(cè)響起首装,我...
    開(kāi)封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杭跪,沒(méi)想到半個(gè)月后仙逻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涧尿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年系奉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姑廉。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缺亮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出桥言,到底是詐尸還是另有隱情萌踱,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布号阿,位于F島的核電站并鸵,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏扔涧。R本人自食惡果不足惜园担,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粉铐,春花似錦疼约、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至汤踏,卻和暖如春织鲸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背溪胶。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工搂擦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哗脖。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓瀑踢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親才避。 傳聞我的和親對(duì)象是個(gè)殘疾皇子橱夭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)桑逝,斷路器棘劣,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 原文:https://my.oschina.net/7001/blog/1619842 摘要: Hystrix是N...
    laosijikaichele閱讀 4,312評(píng)論 0 25
  • (git上的源碼:https://gitee.com/rain7564/spring_microservices_...
    sprainkle閱讀 9,356評(píng)論 13 33
  • “追尋”不僅是一首歌 如果不是因?yàn)榇髮W(xué)里那個(gè)有趣的五月花海的活動(dòng),我們要唱“追尋”這首歌的話楞遏,我可能就想不...
    南__伯閱讀 387評(píng)論 1 2
  • 在來(lái)惠州之前茬暇,完全不知道有這個(gè)城市的存在,完全沒(méi)聽(tīng)說(shuō)過(guò)寡喝。 杭州有飛惠州的航班糙俗,二個(gè)小時(shí)就到了,很方便预鬓。 軍民兩用的...
    好的517閱讀 208評(píng)論 0 0