斷路器,相當於保險絲。
熔斷機制概述
熔斷機制是應對雪崩效應的一種微服務鏈路保護機制。當扇出鏈路的某個微服務出錯不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的調用,快速返回錯誤的響應信息。當檢測到該節點微服務調用響應正常后,恢復調用鏈路。
在Spring Cloud框架里,熔斷機制通過Hystrix實現。Hystrix會監控微服務間調用的狀況,當失敗的調用到一定閾值,缺省是5秒內20次調用失敗,就會啟動熔斷機制。熔斷機制的注解是@HystrixCommand。
在服務層加入相關注解以及配置
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否開啟斷路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 請求次數
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 時間窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失敗率達到多少后跳閘
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
if(id < 0) {
throw new RuntimeException("******id 不能負數");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"調用成功,流水號: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
return "id 不能負數,請稍后再試,/(ㄒoㄒ)/~~ id: " +id;
}
官網步驟:
The precise way that the circuit opening and closing occurs is as follows:
- Assuming the volume across a circuit meets a certain threshold : HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()
- And assuming that the error percentage, as defined above exceeds the error percentage defined in : HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()
- Then the circuit-breaker transitions from CLOSED to OPEN.
- While it is open, it short-circuits all requests made against that circuit-breaker.
- After some amount of time (HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()), the next request is let through. If it fails, the command stays OPEN for the sleep window. If it succeeds, it transitions to CLOSED and the logic in 1) takes over again.
原地址
HystrixCommandProperties配置類
package com.netflix.hystrix;
...
public abstract class HystrixCommandProperties {
private static final Logger logger = LoggerFactory.getLogger(HystrixCommandProperties.class);
/* defaults */
/* package */ static final Integer default_metricsRollingStatisticalWindow = 10000;// default => statisticalWindow: 10000 = 10 seconds (and default of 10 buckets so each bucket is 1 second)
private static final Integer default_metricsRollingStatisticalWindowBuckets = 10;// default => statisticalWindowBuckets: 10 = 10 buckets in a 10 second window so each bucket is 1 second
private static final Integer default_circuitBreakerRequestVolumeThreshold = 20;// default => statisticalWindowVolumeThreshold: 20 requests in 10 seconds must occur before statistics matter
private static final Integer default_circuitBreakerSleepWindowInMilliseconds = 5000;// default => sleepWindow: 5000 = 5 seconds that we will sleep before trying again after tripping the circuit
private static final Integer default_circuitBreakerErrorThresholdPercentage = 50;// default => errorThresholdPercentage = 50 = if 50%+ of requests in 10 seconds are failures or latent then we will trip the circuit
private static final Boolean default_circuitBreakerForceOpen = false;// default => forceCircuitOpen = false (we want to allow traffic)
/* package */ static final Boolean default_circuitBreakerForceClosed = false;// default => ignoreErrors = false
private static final Integer default_executionTimeoutInMilliseconds = 1000; // default => executionTimeoutInMilliseconds: 1000 = 1 second
private static final Boolean default_executionTimeoutEnabled = true;
...
}
熔斷類型
熔斷打開:請求不再進行調用當前服務,內部設置時鍾一般為MTTR(平均故障處理時間),當打開時長達到所設時鍾則進入半熔斷狀態。
熔斷關閉:熔斷關閉不會對服務進行熔斷。
熔斷半開:部分請求根據規則調用當前服務,如果請求成功且符合規則則認為當前服務恢復正常,關閉熔斷。
官網斷路器流程圖
斷路器在什么情況下開始起作用
//=====服務熔斷
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否開啟斷路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 請求次數
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 時間窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失敗率達到多少后跳閘
})
涉及到斷路器的三個重要參數:
- 快照時間窗:斷路器確定是否打開需要統計一些請求和錯誤數據,而統計的時間范圍就是快照時間窗,默認為最近的10秒。
- 請求總數閥值:在快照時間窗內,必須滿足請求總數閥值才有資格熔斷。默認為20,意味着在10秒內,如果該hystrix命令的調用次數不足20次7,即使所有的請求都超時或其他原因失敗,斷路器都不會打開。
- 錯誤百分比閥值:當請求總數在快照時間窗內超過了閥值,比如發生了30次調用,如果在這30次調用中,有15次發生了超時異常,也就是超過50%的錯誤百分比,在默認設定50%閥值情況下,這時候就會將斷路器打開。
斷路器開啟或者關閉的條件
-
到達以下閥值,斷路器將會開啟:
- 當滿足一定的閥值的時候(默認10秒內超過20個請求次數)
- 當失敗率達到一定的時候(默認10秒內超過50%的請求失敗)
-
當開啟的時候,所有請求都不會進行轉發
-
一段時間之后(默認是5秒),這個時候斷路器是半開狀態,會讓其中一個請求進行轉發。如果成功,斷路器會關閉,若失敗,繼續開啟。
斷路器打開之后
1:再有請求調用的時候,將不會調用主邏輯,而是直接調用降級fallback。通過斷路器,實現了自動地發現錯誤並將降級邏輯切換為主邏輯,減少響應延遲的效果。
2:原來的主邏輯要如何恢復呢?
對於這一問題,hystrix也為我們實現了自動恢復功能。
當斷路器打開,對主邏輯進行熔斷之后,hystrix會啟動一個休眠時間窗,在這個時間窗內,降級邏輯是臨時的成為主邏輯,當休眠時間窗到期,斷路器將進入半開狀態,釋放一次請求到原來的主邏輯上,如果此次請求正常返回,那么斷路器將繼續閉合,主邏輯恢復,如果這次請求依然有問題,斷路器繼續進入打開狀態,休眠時間窗重新計時。
All配置
@HystrixCommand(fallbackMethod = "fallbackMethod",
groupKey = "strGroupCommand",
commandKey = "strCommand",
threadPoolKey = "strThreadPool",
commandProperties = {
// 設置隔離策略,THREAD 表示線程池 SEMAPHORE:信號池隔離
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
// 當隔離策略選擇信號池隔離的時候,用來設置信號池的大小(最大並發數)
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 配置命令執行的超時時間
@HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
// 是否啟用超時時間
@HystrixProperty(name = "execution.timeout.enabled", value = "true"),
// 執行超時的時候是否中斷
@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
// 執行被取消的時候是否中斷
@HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
// 允許回調方法執行的最大並發數
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
// 服務降級是否啟用,是否執行回調函數
@HystrixProperty(name = "fallback.enabled", value = "true"),
// 是否啟用斷路器
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 該屬性用來設置在滾動時間窗中,斷路器熔斷的最小請求數。例如,默認該值為 20 的時候,如果滾動時間窗(默認10秒)內僅收到了19個請求, 即使這19個請求都失敗了,斷路器也不會打開。
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
// 該屬性用來設置在滾動時間窗中,表示在滾動時間窗中,在請求數量超過 circuitBreaker.requestVolumeThreshold 的情況下,如果錯誤請求數的百分比超過50, 就把斷路器設置為 "打開" 狀態,否則就設置為 "關閉" 狀態。
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
// 該屬性用來設置當斷路器打開之后的休眠時間窗。 休眠時間窗結束之后,會將斷路器置為 "半開" 狀態,嘗試熔斷的請求命令,如果依然失敗就將斷路器繼續設置為 "打開" 狀態,如果成功就設置為 "關閉" 狀態。
@HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
// 斷路器強制打開
@HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
// 斷路器強制關閉
@HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
// 滾動時間窗設置,該時間用於斷路器判斷健康度時需要收集信息的持續時間
@HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
// 該屬性用來設置滾動時間窗統計指標信息時划分"桶"的數量,斷路器在收集指標信息的時候會根據設置的時間窗長度拆分成多個 "桶" 來累計各度量值,每個"桶"記錄了一段時間內的采集指標。
// 比如 10 秒內拆分成 10 個"桶"收集這樣,所以 timeinMilliseconds 必須能被 numBuckets 整除。否則會拋異常
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
// 該屬性用來設置對命令執行的延遲是否使用百分位數來跟蹤和計算。如果設置為 false, 那么所有的概要統計都將返回 -1。
@HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
// 該屬性用來設置百分位統計的滾動窗口的持續時間,單位為毫秒。
@HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
// 該屬性用來設置百分位統計滾動窗口中使用 “ 桶 ”的數量。
@HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
// 該屬性用來設置在執行過程中每個 “桶” 中保留的最大執行次數。如果在滾動時間窗內發生超過該設定值的執行次數,
// 就從最初的位置開始重寫。例如,將該值設置為100, 滾動窗口為10秒,若在10秒內一個 “桶 ”中發生了500次執行,
// 那么該 “桶” 中只保留 最后的100次執行的統計。另外,增加該值的大小將會增加內存量的消耗,並增加排序百分位數所需的計算時間。
@HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
// 該屬性用來設置采集影響斷路器狀態的健康快照(請求的成功、 錯誤百分比)的間隔等待時間。
@HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
// 是否開啟請求緩存
@HystrixProperty(name = "requestCache.enabled", value = "true"),
// HystrixCommand的執行和事件是否打印日志到 HystrixRequestLog 中
@HystrixProperty(name = "requestLog.enabled", value = "true"),
},
threadPoolProperties = {
// 該參數用來設置執行命令線程池的核心線程數,該值也就是命令執行的最大並發量
@HystrixProperty(name = "coreSize", value = "10"),
// 該參數用來設置線程池的最大隊列大小。當設置為 -1 時,線程池將使用 SynchronousQueue 實現的隊列,否則將使用 LinkedBlockingQueue 實現的隊列。
@HystrixProperty(name = "maxQueueSize", value = "-1"),
// 該參數用來為隊列設置拒絕閾值。 通過該參數, 即使隊列沒有達到最大值也能拒絕請求。
// 該參數主要是對 LinkedBlockingQueue 隊列的補充,因為 LinkedBlockingQueue 隊列不能動態修改它的對象大小,而通過該屬性就可以調整拒絕請求的隊列大小了。
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
}
)
步驟說明
- 創建HystrixCommand (用在依賴的服務返回單個操作結果的時候)或HystrixObserableCommand(用在依賴的服務返回多個操作結果的時候)對象。
- 命令執行。
- 其中 HystrixCommand實現了下面前兩種執行方式
- execute():同步執行,從依賴的服務返回一個單一的結果對象或是在發生錯誤的時候拋出異常。
- queue():異步執行,直接返回一個Future對象,其中包含了服務執行結束時要返回的單一結果對象。
- 而 HystrixObservableCommand實現了后兩種執行方式:
- obseve():返回Observable對象,它代表了操作的多個統
果,它是一個Hot Observable (不論“事件源”是否有“訂閱者”,都會在創建后對事件進行發布,所以對於Hot Observable的每一個“訂閱者”都有可能是從“事件源”的中途開始的,並可能只是看到了整個操作的局部過程)。 - toObservable():同樣會返回Observable對象,也代表了操作的多個結果,但它返回的是一個Cold Observable(沒有“訂間者”的時候並不會發布事件,而是進行等待,直到有“訂閱者"之后才發布事件,所以對於Cold Observable 的訂閱者,它可以保證從一開始看到整個操作的全部過程)。
- obseve():返回Observable對象,它代表了操作的多個統
- 若當前命令的請求緩存功能是被啟用的,並且該命令緩存命中,那么緩存的結果會立即以Observable對象的形式返回。
- 檢查斷路器是否為打開狀態。如果斷路器是打開的,那么Hystrix不會執行命令,而是轉接到fallback處理邏輯(第8步);如果斷路器是關閉的,檢查是否有可用資源來執行命令(第5步)。
- 線程池/請求隊列信號量是否占滿。如果命令依賴服務的專有線程地和請求隊列,或者信號量(不使用線程的時候)已經被占滿,那么Hystrix也不會執行命令,而是轉接到fallback處理理輯(第8步) 。
- Hystrix會根據我們編寫的方法來決定采取什么樣的方式去請求依賴服務。
- HystrixCommand.run():返回一個單一的結果,或者拋出異常。
- HystrixObservableCommand.construct():返回一個Observable對象來發射多個結果,或通過onError發送錯誤通知。
- Hystix會將“成功”、“失敗”、“拒絕”、“超時” 等信息報告給斷路器,而斷路器會維護一組計數器來統計這些數據。斷路器會使用這些統計數據來決定是否要將斷路器打開,來對某個依賴服務的請求進行"熔斷/短路"。
- 當命令執行失敗的時候,Hystix會進入fallback嘗試回退處理,我們通常也稱波操作為“服務降級”。而能夠引起服務降級處理的情況有下面幾種:
- 第4步∶當前命令處於“熔斷/短路”狀態,斷洛器是打開的時候。
- 第5步∶當前命令的錢程池、請求隊列或者信號量被占滿的時候。
- 第6步∶HystrixObsevableCommand.construct()或HytrixCommand.run()拋出異常的時候。
- 當Hystrix命令執行成功之后,它會將處理結果直接返回或是以Observable的形式返回。
tips:如果我們沒有為命令實現降級邏輯或者在降級處理邏輯中拋出了異常,Hystrix依然會運回一個Obsevable對象,但是它不會發射任結果數慣,而是通過onError方法通知命令立即中斷請求,並通過onError方法將引起命令失敗的異常發送給調用者。