1.背景
目前對于一些非核心操作,如增減庫存後儲存記錄檔 發送異步消息時(具體業務流程),一旦出現MQ服務異常時,會導緻接口響應逾時,是以可以考慮對非核心操作引入服務降級、服務隔離。
2.Hystrix說明
官方文檔 [https://github.com/Netflix/Hystrix/wiki]
hystrix是netflix開源的一個容災架構,解決當外部依賴故障時拖垮業務系統、甚至引起雪崩的問題。
在大中型分布式系統中,通常系統很多依賴(HTTP,hession,Netty,Dubbo等),在高并發通路下,這些依賴的穩定性與否對系統的影響非常大,但是依賴有很多不可控問題:如網絡連接配接緩慢,資源繁忙,暫時不可用,服務脫機等。
當依賴阻塞時,大多數伺服器的線程池就出現阻塞(BLOCK),影響整個線上服務的穩定性,在複雜的分布式架構的應用程式有很多的依賴,都會不可避免地在某些時候失敗。高并發的依賴失敗時如果沒有隔離措施,目前應用服務就有被拖垮的風險。
<code>例如:一個依賴30個SOA服務的系統,每個服務99.99%可用。 99.99%的30次方 ≈ 99.7% 0.3% 意味着一億次請求 會有 3,000,00次失敗 換算成時間大約每月有2個小時服務不穩定. 随着服務依賴數量的變多,服務不穩定的機率會成指數性提高.</code>
解決問題方案:對依賴做隔離。
想要知道如何使用,必須先明白其核心設計理念,Hystrix基于指令模式

Command是在Receiver和Invoker之間添加的中間層,Command實作了對Receiver的封裝
那麼Hystrix的應用場景如何與上圖對應呢?
API既可以是Invoker又可以是reciever,通過繼承Hystrix核心類HystrixCommand來封裝這些API(例如,遠端接口調用,資料庫查詢之類可能會産生延時的操作)。就可以為API提供彈性保護了。
1: Hystrix使用指令模式HystrixCommand(Command)包裝依賴調用邏輯,每個指令在單獨線程中/信号授權下執行。
2: 可配置依賴調用逾時時間,逾時時間一般設為比99.5%平均時間略高即可.當調用逾時時,直接傳回或執行fallback邏輯。
3: 為每個依賴提供一個小的線程池(或信号),如果線程池已滿調用将被立即拒絕,預設不采用排隊.加速失敗判定時間。
4: 依賴調用結果分:成功,失敗(抛出異常),逾時,線程拒絕,短路。 請求失敗(異常,拒絕,逾時,短路)時執行fallback(降級)邏輯。
5: 提供熔斷器元件,可以自動運作或手動調用,停止目前依賴一段時間(10秒),熔斷器預設錯誤率門檻值為50%,超過将自動運作。
6: 提供近實時依賴的統計和監控
一個HystrixCommand或一個HystrixObservableCommand對象
<code>代表了對某個依賴服務發起的一次請求或者調用</code>
同時在構造函數中傳入所有需要的參數
HystrixCommand主要用于僅僅會傳回一個結果的調用
HystrixObservableCommand主要用于可能會傳回多條結果的調用
要執行Command,需要在4個方法中選擇其中的一個:execute(),queue(),observe(),toObservable()。其中execute()和queue()僅僅對HystrixCommand适用
execute():調用後直接block,屬于同步調用,直到依賴服務傳回單條結果,或者抛出異常
queue():傳回一個Future,屬于異步調用,後面可以通過Future擷取單條結果
observe():訂閱一個Observable對象,Observable代表的是依賴服務傳回的結果,擷取到一個那個代表結果的 Observable對象的拷貝對象
toObservable():傳回一個Observable對象,如果我們訂閱這個對象,就會執行command并且擷取傳回結果
如果這個command開啟了請求緩存<code>request cache</code>,而且這個調用的結果在緩存中存在,那麼直接從緩存中傳回Observer結果。
一般來說,所謂的request cache隻是針對一次request context,類似于web應用中響應一個請求去調用多個依賴服務,對同一個依賴的相同參數的調用可以放到緩存進而減少網絡請求來提升性能。
具體實作可以通過添加過濾器來添加一個HystrixRequestContext
在Command實作中有CacheKey的設定方法:
在指令結果沒有緩存命中的時候, Hystrix 會在執行指令前檢查斷路器是否為打開狀态
即檢查這個command對應的依賴服務是否開啟了斷路器
斷路器被打開,不執行該command,直接執行fallback降級機制
斷路器被關閉,第5步
斷路器的實作原理:
控制短路器是否允許工作,包括跟蹤依賴服務調用的健康狀況,以及對異常情況過多時是否允許觸發短路,預設是true,一般不需要修改
隻要執行一個command,這個請求就一定會經過斷路器
如果在一定時間内經過斷路器的流量超過門檻值,才會進行後面的斷路器相關處理
可以通過以下配置修改,預設值是20
如果斷路器統計到的異常調用的占比超過了一定的門檻值,才會打開斷路器開關
預設是50%的異常比例
經過以上步驟,然後斷路器<code>從close狀态轉換到open狀态</code>
斷路器打開的時候,所有經過該斷路器的請求全部被短路,不調用後端服務,直接走fallback降級(第8步)
經過了一段時間之後,<code>斷路器會進入half-open狀态</code>,讓一條請求經過斷路器,看能不能正常調用
如果調用成功了,那麼就自動恢複,<code>轉到close狀态</code>
時間可以通過以下配置來修改,預設為5000毫秒
可以強迫打開短路器,一般不使用
強迫關閉短路器,一般不使用
如果command對應的線程池/隊列/semaphore(不使用線程池時)已經滿了,那麼也不會執行command
<code>直接去調用fallback降級機制</code>
調用HystrixObservableCommand.construct()或HystrixCommand.run()來實際執行這個command
HystrixCommand.run() 傳回單條結果,或者抛出一個異常
HystrixObservableCommand.construct() 傳回一個Observable對象,可以擷取多條結果或者 onError 發送錯誤通知
如果HystrixCommand.run()或HystrixObservableCommand.construct()的執行,超過了timeout
那麼command所在的線程就會抛出一個TimeoutException,會去執行fallback降級機制,而且就不會管run()或construct()傳回的值
這裡要注意的一點是,我們是不可能終止掉一個調用嚴重延遲的依賴服務的線程的,隻能說給你抛出來一個TimeoutException,但是還是可能會因為嚴重延遲的調用線程占滿整個線程池的,即使這個時候新來的流量都被限流了。如果沒有timeout的話,那麼就會拿到一些調用依賴服務擷取到的結果,然後hystrix會做一些logging記錄和metric統計。
有一個很重要的點,command的執行強烈建議我們設定一個timeout的時間,來避免所有資源都被占用導緻系統整體性能下降,可以通過以下來配置:
當command執行逾時之後會直接進行fallback降級處理
Hystrix會将每一個依賴服務的調用成功,失敗,拒絕,逾時,等事件,都會發送給circuit breaker斷路器
短路器就會對調用成功/失敗/拒絕/逾時等事件的次數進行統計。短路器會根據這些統計次數來決定,是否要進行短路,如果打開了短路器,那麼在一段時間内就會直接短路,然後如果在之後第一次檢查發現調用成功了,就關閉斷路器
一般來說有四種情況會調用fallback降級機制
Hystrix調用各種外部接口,或者通路外部依賴,mysql,redis,zookeeper,kafka,等等出現了任何異常
對外部的依賴調用所使用的線程池已滿/信号量限流資源到達極限
通路時間過長,可能就會導緻逾時,報一個TimeoutException異常
基于上述三種情況都會發送異常事件到斷路器中去進行統計,如果異常達到一定的比例直接開啟斷路器
兩種常見的降級處理是
維護記憶體ECache直接擷取一份過期的資料
設定一個預設值傳回
HystrixObservableCommand,是實作resumeWithFallback方法
一般在降級機制中,都建議給出一些預設的傳回值,比如靜态的一些代碼邏輯,或者從記憶體中的緩存中提取一些資料,盡量在這裡不要再進行網絡請求了。即使在降級中,一定要進行網絡調用,也應該将那個調用放在一個HystrixCommand中,進行隔離
設定fallback.isolation.semaphore.maxConcurrentRequests,這個參數設定了HystrixCommand.getFallback()最大允許的并發請求數量,預設值是10,也是通過semaphore信号量的機制去限流。如果超出了這個最大值,那麼直接被reject
每個熔斷器預設維護10個bucket,每秒一個bucket,每個bucket記錄成功,失敗,逾時,拒絕的狀态
預設錯誤超過50%且10秒内超過20個請求進行中斷攔截.
Hystrix隔離方式采用線程/信号的方式,通過隔離限制依賴的并發量和阻塞擴散
把執行依賴代碼的線程與請求線程分離,請求線程可以自由控制離開的時間(異步)
通過線程池大小可以控制并發量,當線程池飽和時可以提前拒絕服務,防止依賴問題擴散
線上建議線程池不要設定過大,否則大量堵塞線程有可能會拖慢伺服器
優點
使用線程可以完全隔離第三方代碼,請求線程可以快速傳回
當一個失敗的依賴再次變成可用時,線程池将清理,并立即恢複可用,而不是一個長時間的恢複
可以完全模拟異步調用,友善異步程式設計
缺點
線程池的主要缺點是它增加了cpu,因為每個指令的執行涉及到排隊(預設使用SynchronousQueue避免排隊),排程和上下文切換。
對使用ThreadLocal等依賴線程狀态的代碼增加複雜性,需要手動傳遞和清理線程狀态。
NOTE: Netflix公司内部認為線程隔離開銷足夠小,不會造成重大的成本或性能的影響。
Netflix 内部API 每天100億的HystrixCommand依賴請求使用線程隔,每個應用大約40多個線程池,每個線程池大約5-20個線程。
信号隔離也可以用于限制并發通路,防止阻塞擴散, 與線程隔離最大不同在于執行依賴代碼的線程依然是請求線程(該線程需要通過信号申請)
如果用戶端是可信的且可以快速傳回,可以使用信号隔離替換線程隔離,降低開銷.
信号量的大小可以動态調整, 線程池大小不可以.
4 參數配置
其他參數可參見 https://github.com/Netflix/Hystrix/wiki/Con
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-jYpgq4bC-1575200740143)(http://upload-images.jianshu.io/upload_images/4685968-719e2df25db06499.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 “QQ20170715-235020@2x”)]