一、問題產生
雪崩效應:是一種因服務提供者的不可用導致服務調用者的不可用,並將不可用逐漸放大的過程
正常情況下的服務:
某一服務出現異常,拖垮整個服務鏈路,消耗整個線程隊列,造成服務不可用,資源耗盡:
形成過程:
1)服務提供者不可用
a)硬件故障:硬件損壞造成的服務器主機宕機, 網絡硬件故障造成的服務提供者的不可訪問
b)程序Bug:
c) 緩存擊穿:緩存擊穿一般發生在緩存應用重啟, 所有緩存被清空時,以及短時間內大量緩存失效時. 大量的緩存不命中, 使請求直擊后端,造成服務提供者超負荷運行,引起服務不可用
d)用戶大量請求:在秒殺和大促開始前,如果准備不充分,用戶發起大量請求也會造成服務提供者的不可用
2)重試加大流量
a)用戶重試:在服務提供者不可用后, 用戶由於忍受不了界面上長時間的等待,而不斷刷新頁面甚至提交表單
b)代碼邏輯重試: 服務調用端的會存在大量服務異常后的重試邏輯
3)服務調用者不可用
a)同步等待造成的資源耗盡:當服務調用者使用同步調用 時, 會產生大量的等待線程占用系統資源. 一旦線程資源被耗盡,服務調用者提供的服務也將處於不可用狀態, 於是服務雪崩效應產生了。
二、概念
- 服務熔斷:
一般是指軟件系統中,由於某些原因使得服務出現了過載現象,為防止造成整個系統故障,從而采用的一種保護措施,所以很多地方把熔斷亦稱為過載保護。很多時候剛開始可能只是系統出現了局部的、小規模的故障,然而由於種種原因,故障影響的范圍越來越大,最終導致了全局性的后果。
適用場景:防止應用程序直接調用那些很可能會調用失敗的遠程服務或共享資源 - 服務降級:
當服務器壓力劇增的情況下,根據當前業務情況及流量對一些服務和頁面有策略的降級,以此釋放服務器資源以保證核心任務的正常運行。
三、工作原理
Hystrix 是一個幫助解決分布式系統交互時超時處理和容錯的類庫, 它同樣擁有保護系統的能力。Netflix的眾多開源項目之一。
1. 隔離:
Hystrix隔離方式采用線程/信號的方式,通過隔離限制依賴的並發量和阻塞擴散
1)線程隔離
Hystrix在用戶請求和服務之間加入了線程池。
Hystrix為每個依賴調用分配一個小的線程池,如果線程池已滿調用將被立即拒絕,默認不采用排隊.加速失敗判定時間。線程數是可以被設定的。
原理:用戶的請求將不再直接訪問服務,而是通過線程池中的空閑線程來訪問服務,如果線程池已滿,則會進行降級處理,用戶的請求不會被阻塞,至少可以看到一個執行結果(例如返回友好的提示信息),而不是無休止的等待或者看到系統崩潰。
隔離前:
隔離后:
b)信號隔離:
信號隔離也可以用於限制並發訪問,防止阻塞擴散, 與線程隔離最大不同在於執行依賴代碼的線程依然是請求線程(該線程需要通過信號申請, 如果客戶端是可信的且可以快速返回,可以使用信號隔離替換線程隔離,降低開銷。信號量的大小可以動態調整, 線程池大小不可以。(參考文章2)
2. 熔斷:
如果某個目標服務調用慢或者有大量超時,此時,熔斷該服務的調用,對於后續調用請求,不在繼續調用目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復調用。
熔斷器:Circuit Breaker
熔斷器是位於線程池之前的組件。用戶請求某一服務之后,Hystrix會先經過熔斷器,此時如果熔斷器的狀態是打開(跳起),則說明已經熔斷,這時將直接進行降級處理,不會繼續將請求發到線程池。熔斷器相當於在線程池之前的一層屏障。每個熔斷器默認維護10個bucket ,每秒創建一個bucket ,每個blucket記錄成功,失敗,超時,拒絕的次數。當有新的bucket被創建時,最舊的bucket會被拋棄。
熔斷器的狀態機:
- Closed:熔斷器關閉狀態,調用失敗次數積累,到了閾值(或一定比例)則啟動熔斷機制;
- Open:熔斷器打開狀態,此時對下游的調用都內部直接返回錯誤,不走網絡,但設計了一個時鍾選項,默認的時鍾達到了一定時間(這個時間一般設置成平均故障處理時間,也就是MTTR),到了這個時間,進入半熔斷狀態;
- Half-Open:半熔斷狀態,允許定量的服務請求,如果調用都成功(或一定比例)則認為恢復了,關閉熔斷器,否則認為還沒好,又回到熔斷器打開狀態;
四、流程
流程說明:
1
:每次調用創建一個新的HystrixCommand,把依賴調用封裝在run()方法中。
2
:執行execute()/queue做同步或異步調用。
3
:判斷熔斷器(circuit-breaker)是否打開,如果打開跳到步驟
8,
進行降級策略,如果關閉進入步驟。
4
:判斷線程池/隊列/信號量是否跑滿,如果跑滿進入降級步驟
8,
否則繼續后續步驟。
5
:調用HystrixCommand的run方法。運行依賴邏輯
5a:依賴邏輯調用超時,進入步驟
8。
6
:判斷邏輯是否調用成功
6a:返回成功調用結果
6b:調用出錯,進入步驟
8。
7
:計算熔斷器狀態,所有的運行狀態(成功, 失敗, 拒絕,超時)上報給熔斷器,用於統計從而判斷熔斷器狀態。
8
:getFallback()降級邏輯。
以下四種情況將觸發getFallback調用:
(
1
):run()方法拋出非HystrixBadRequestException異常
(
2
):run()方法調用超時
(
3
):熔斷器開啟攔截調用
(
4
):線程池/隊列/信號量是否跑滿
8a:沒有實現getFallback的Command將直接拋出異常
8b:fallback降級邏輯調用成功直接返回
8c:降級邏輯調用失敗拋出異常
9
:返回執行成功結果
五、執行方式
同步執行:即一旦開始執行該命令,當前線程就得阻塞着直到該命令返回結果,然后才能繼續執行下面的邏輯
異步執行:命令開始執行會返回一個Future<T>的對象,不阻塞后面的邏輯,開發者自己根據需要去獲取結果。
響應式執行:命令開始執行會返回一個Observable<T> 對象,開發者可以給給Obeservable對象注冊上Observer或者Action1對象,響應式地處理命令執行過程中的不同階段。當調用HystrixCommand的observe()方法,或使用Observable的工廠方法(just(),from())即為響應式執行,這個功能的實現是基於Netflix的另一個開源項目RxJava(https://github.com/Netflix/RxJava)來的,更細節的用法可以參考:https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-Reactive-Execution。
六、代碼實現
jar包:
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>
1.4
.
21
</version>
</dependency>
|
參數
|
作用
|
備注
|
---|---|---|
circuitBreaker.errorThresholdPercentage |
失敗率達到多少百分比后熔斷 | 默認值:50 主要根據依賴重要性進行調整 |
circuitBreaker.forceClosed |
是否強制關閉熔斷 | 如果是強依賴,應該設置為true |
circuitBreaker.requestVolumeThreshold |
熔斷觸發的最小個數/10s | 默認值:20 |
circuitBreaker.sleepWindowInMilliseconds |
熔斷多少秒后去嘗試請求 | 默認值:5000 |
commandKey |
默認值:當前執行方法名 | |
coreSize |
線程池coreSize | 默認值:10 |
execution.isolation.semaphore.maxConcurrentRequests |
信號量最大並發度 | SEMAPHORE模式有效,默認值:10 |
execution.isolation.strategy |
隔離策略,有THREAD和SEMAPHORE | 默認使用THREAD模式,以下幾種可以使用SEMAPHORE模式:
|
execution.isolation.thread.interruptOnTimeout |
是否打開超時線程中斷 | THREAD模式有效 |
execution.isolation.thread.timeoutInMilliseconds
|
超時時間 | 默認值:1000 在THREAD模式下,達到超時時間,可以中斷 在SEMAPHORE模式下,會等待執行完成后,再去判斷是否超時 |
execution.timeout.enabled |
是否打開超時 | |
fallback.isolation.semaphore.maxConcurrentRequests |
fallback最大並發度 | 默認值:10 |
groupKey |
表示所屬的group,一個group共用線程池 | 默認值:getClass().getSimpleName(); |
maxQueueSize |
請求等待隊列 | 默認值:-1 如果使用正數,隊列將從SynchronizeQueue改為LinkedBlockingQueue |
hystrix.command.default.metrics.rollingStats.timeInMilliseconds | 設置統計的時間窗口值的,毫秒值 | circuit break 的打開會根據1個rolling window的統計來計算。若rolling window被設為10000毫秒,則rolling window會被分成n個buckets,每個bucket包含success,failure,timeout,rejection的次數的統計信息。默認10000 |
hystrix.command.default.metrics.rollingStats.numBuckets | 設置一個rolling window被划分的數量 | |
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds | 記錄health 快照(用來統計成功和錯誤綠)的間隔,默認500ms |
七、參考文章
官方文檔:https://github.com/Netflix/Hystrix/wiki
同步異步反應執行三種方式示例:Hystrix使用&設置准則#2.使用
http://mvnrepository.com/artifact/com.netflix.hystrix/hystrix-javanica