1. Hystrix斷路器概述
1.1 分散式系統面臨的問題
複雜分散式體系結構中的應用程式有數十個依賴關係,每個依賴關係在某些時候將不可避免地失敗。這就造成有可能會發生服務雪崩。那麼什麼是服務雪崩呢?
多個微服務之間呼叫的時候,假設微服務A呼叫微服務B和微服務C,微服務B和微服務C又呼叫其他的微服務,這就是所謂的“扇出”(像一把開啟的摺扇)。如果扇出的鏈路上某個微服務的呼叫響應時間過長或者不可用,對微服務A的呼叫就會佔用越來越多的系統資源,進而引起系統崩潰,這就是所謂的”雪崩效應“。也就是系統的 高可用 受到了破壞。
對於高流量的應用來說,單一的後端依賴可能會導致所有伺服器上的所有資源在幾秒內飽和。比失敗更糟的是,這些應用程式還可能導致 服務之間的延遲增加,備份佇列,執行緒和其他系統資源緊張,導致整個系統傳送更過的級聯故障。這些都表示需要對故障和延遲進行隔離和管理,以便 單個依賴關係的失敗,不能取消整個應用程式或系統。
所以,通常當發現一個模組下的某個例項失敗後,這時候這個模組依然還會接受流量,然後這個有問題的模組還呼叫了其他的模組,這樣就會發生級聯故障,或者叫做雪崩。而面對這種糟糕的問題,我們就應該採取 服務降級、服務熔斷 等方式來解決。
1.2 Hystrix是什麼
Hystrix是一個用於處理分散式系統的 延遲
和 容錯
的開源庫,在分散式系統裡,許多依賴不可避免的會呼叫失敗,比如超時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整個服務失敗,避免級聯故障,以提高分散式系統的彈性。
“斷路器”本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似於物理的熔斷保險絲),向呼叫方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者丟擲呼叫方無法處理的異常,這樣就保證了服務呼叫方的執行緒不會被長時間、不必要的佔用,從而避免了故障在分散式系統中的蔓延,乃至雪崩。
1.3 Hystrix作用
主要有服務降級、服務熔斷、接近實時的監控、限流、隔離等等,其官方文件參考。當然Hystrix現在已經停更了,雖然有一些替代品,但是學習Hystrix及其裡面的思想還是非常重要的!
1.4 Hystrix重要概念
服務降級(Fall Back)
假設微服務A要呼叫的服務B不可用了,需要服務B提供一個兜底的解決方法,而不是讓服務A在那裡傻等,耗死。不讓客戶端等待並立刻返回一個友好提示,比如像客戶端提示伺服器忙,請稍後再試等。哪些情況會觸發服務降級呢?比如 程式執行異常
、超時
、服務熔斷觸發服務降級
、執行緒池/訊號量打滿
也會導致服務降級。
服務熔斷(Break)
服務熔斷就相當於物理上的熔斷保險絲。類比保險絲達到最大服務訪問後,直接拒絕訪問,拉閘斷電,然後呼叫服務降級的方法並返回友好提示。
**服務限流(Flow Limit)
** 秒殺高併發等操作,嚴禁一窩蜂的過來擁擠,大家排隊,一秒鐘N個,有序進行。
2. Hystrix案例
2.1 服務提供者8003模組
建Module
cloud-provider-hystrix-payment8003
pom.xml
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
yml配置檔案 / 主啟動類
server:
port: 8003
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetchRegistry: true
service-url:
# 單機版
defaultZone: http://localhost:7001/eureka # 入駐的服務註冊中心地址
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8003 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8003.class,args);
}
}
業務類
- service
@Service
public class PaymentService {
/**能夠正常訪問的方法*/
public String paymentInfo_OK(Integer id) {
return "執行緒池: " + Thread.currentThread().getName()
+ " paymentInfo_OK,id: " + id + "\t" + "哈~";
}
/**模擬出錯的方法*/
public String paymentInfo_FAIL(Integer id) {
int timeNumber = 3;
//暫停幾秒鐘執行緒,程式本身沒有錯誤,就是模擬超時
try {
TimeUnit.SECONDS.sleep(timeNumber);
}catch (InterruptedException e) {
e.printStackTrace();
}
return "執行緒池: " + Thread.currentThread().getName()
+ " paymentInfo_FAIL,id: " + id + "\t" + "哈~ "
+ "耗時" + timeNumber + "s";
}
}
- controller
@RestController
@Slf4j
@RequestMapping("/payment/hystrix")
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("$server.port")
private String serverPort;
@GetMapping("ok/{id}")
public String paymentInfo_OK(@PathVariable("id")Integer id) {
String result = paymentService.paymentInfo_OK(id);
log.info("===> result: " + result);
return result;
}
@GetMapping("timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id")Integer id) {
String result = paymentService.paymentInfo_FAIL(id);
log.info("===> result: " + result);
return result;
}
}
也就是說cloud-provider-hystrix-payment服務提供了兩個方法,paymentInfo_OK 方法可以很快訪問,paymentInfo_TimeOut 方法我們模擬了一個複雜的業物邏輯,通過執行緒休眠的方式使其模擬一個需要執行3秒的服務方法。
在啟動了註冊中心和8003服務後,我們對服務的 paymentInfo_OK(下面用OK代替) 和 paymentInfo_TimeOut(下面用TO代替)分別進行訪問,我們發現http://localhost:8003/payment/hystrix/ok/1 可以很快的訪問,而http://localhost:8003/payment/hystrix/timeout/1 每次訪問大約需要3秒的時間。
上述為根基平臺,開始演示:正確 => 錯誤 => 降級熔斷 => 正確
2.2 8003高併發測試
服務提供方自測壓力測試:
需要3秒的複雜業務邏輯 TO 訪問時,需要時間很少的 OK 是完全可以正常訪問的,但是在高併發的情況下,也就是說 TO 有很多訪問量的時候,OK 還能夠這麼正常的訪問嗎?下面我們用 Jmeter 進行高併發壓力測試,用20000個請求都去訪問 TO 服務。
在 Jmeter 中新建一個執行緒組:測試Hystrix用來模擬高併發訪問 TO 服務,執行緒組配置引數如下:
然後我們用該執行緒組傳送HTTP請求給 TO 服務,建立如下的HTTP請求進行壓力測試:
我們觀察8001服務的後端控制檯,可以看到對 TO 服務進行了大量的訪問。而此時我們再去訪問 OK 服務時什麼樣的呢?=> 可以看到,OK 服務無法像之前一樣很快能夠得到訪問,這裡我們模擬的是20000的訪問量(沒敢模擬數字太大的訪問量,怕把系統直接搞死),實際中可能會有遠大於20000的訪問量,當訪問量更多的時候,甚至可能卡死服務,原因就是 Tomcat的預設的工作執行緒數被打滿了,沒有多餘的執行緒來分解壓力和處理。
而剛才做的壓力測試還只是服務提供方8001自己實現的測試,如果此時是外部的服務消費方80來訪問該服務,那麼服務消費方只能夠進行乾等,消費方顯然會對這樣的等待時間不滿意,服務提供方很有可能直接被拖死。我們發現8001自測都會出現問題,那如果我們再用服務消費方測試呢?
2.3 服務消費者80模組進行壓力測試
新建一個Module:cloud-consumer-feign-hystrix-order80作為服務消費方,服務消費方利用feign訪問提供方的服務,編寫對應的service介面如下:
@Component
@FeignClient("CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_FAIL(@PathVariable("id") Integer id);
}
然後編寫其Controller:
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_FAIL(id);
return result;
}
}
將80服務啟動,用http://localhost/consumer/payment/hystrix/ok/1 對服務提供方的 OK 服務進行訪問,再進行壓力測試,和之前一樣,無法迅速訪問該服務,如果壓力測試中的執行緒數更多的時候,很可能會造成超時錯誤,出現以下錯誤提示:
故障原因:8003同一層次的其他介面服務被困死,因為Tomcat執行緒池裡面的工作執行緒已經被擠佔完畢,80此時再呼叫8003,必然導致客戶端訪問響應緩慢。正是因為出現了這種現象,所以我們才需要 服務降級、容錯、服務限流 等技術。
2.4 發現問題
要解決的問題:
- 超時導致伺服器變慢(轉圈) => 超時不再等待
- 出錯(當機或程式執行出錯) => 出錯要有兜底
解決方案:
- 對方服務(8003)超時了,呼叫者(80)不能一直卡死等待,必須有服務降級
- 對方服務(8003)當機了,呼叫者(80)不能一直卡死等待,必須有服務降級
- 對方服務(8003)OK,呼叫者(80)自己出故障或有自我要求(自己的等待時間小於服務提供者),自己處理降級
3. Hystrix服務降級Fall Back
3.1 服務端服務提供方的服務降級
降級的配置用 @HystrixCommand
註解,在服務提供方自身找問題,設定自身呼叫超時時間的峰值,在峰值內可以正常執行,超過了峰值需要有兜底的方法處理,用作服務降級。
首先在服務提供方的業務類上啟用 @HystrixCommand
實現報異常後如何處理,也就是一旦呼叫服務方法失敗並丟擲了錯誤資訊後,會自動呼叫 @HystrixCommand
標註好的fallbackMethod服務降級方法。在服務提供方的service中我們修改 TO 服務:
@Service
public class PaymentService {
/**能夠正常訪問的方法*/
public String paymentInfo_OK(Integer id) {
return "執行緒池: " + Thread.currentThread().getName()
+ " paymentInfo_OK,id: " + id + "\t" + "哈~";
}
/**模擬出錯的方法*/
@HystrixCommand(fallbackMethod = "paymentInfo_FailHandler",
commandProperties = {
@HystrixProperty(
name = "execution.isolation.thread.timeoutInMilliseconds",
value = "3000")
})
public String paymentInfo_FAIL(Integer id) {
//int age = 10/0; //模擬異常,也會兜底
int timeNumber = 5;
//暫停幾秒鐘執行緒,程式本身沒有錯誤,就是模擬超時
try {
TimeUnit.SECONDS.sleep(timeNumber);
}catch (InterruptedException e) {
e.printStackTrace();
}
return "執行緒池: " + Thread.currentThread().getName()
+ " paymentInfo_FAIL,id: " + id + "\t" + "哈~ "
+ "耗時" + timeNumber + "s";
}
/** 定製服務降級方法*/
public String paymentInfo_FailHandler(Integer id) {
return "系統忙,請稍後再試";
}
}
然後在主啟動類上新增 @EnableCircuitBreaker
註解對熔斷器進行啟用,TO服務的訪問時間5秒,而我們用Hystrix配置的時間峰值為3秒,也就是當服務超時或服務出錯時,會訪問我們設定的fallbackMethod服務降級方法,再次訪問TO服務,我們發現其執行的方法確實為服務降級方法:
注意:我們自己配置過的熱部署方式對Java程式碼的改動明顯,但對 @HystrixCommand內屬性的修改建議重啟微服務(有時不能及時生效)
3.2 客戶端服務消費方的服務降級
既然服務的提供方可以進行降級保護,那麼服務的消費方,也可以更好的保護自己,也可以對自己進行降級保護,也就是說Hystrix服務降級既可以放在服務端(服務提供方),也可以放在客戶端(服務消費方),但是!通常是用客戶端做服務降級,下面在服務消費方即客戶端配置自己的服務降級保護,修改80消費方的配置檔案,新增如下配置已使其支援Hystrix:
feign:
hystrix:
enabled: true
在80消費方的主啟動類上新增 @EnableHystrix
啟用Hystrix服務。然後在80的Controller中同樣加入@HystrixCommand
註解已實現服務降級:
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentInfo_FailHandler",
commandProperties = {
@HystrixProperty(
name = "execution.isolation.thread.timeoutInMilliseconds",
value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_FAIL(id);
return result;
}
/** 定製服務降級方法*/
public String paymentInfo_FailHandler(Integer id) {
return "我是消費者80,系統忙,請稍後再試";
}
}
也就是說如果消費方訪問服務提供方的時間超過了1.5秒,那麼就會訪問自己的降級服務方法。
3.3 統一全域性服務降級方法
而當前的這種處理方式是有問題的,也就是每個業務方法都對應了一個服務降級方法,這會導致程式碼膨脹,所以我們應該定義一個統一的服務降級方法,統一的方法和自定義的方法分開。而且我們將服務降級方法和業務邏輯混合在了一起,這會導致程式碼混亂,業務邏輯不清晰。
對於第一個問題,我們可以用feign介面中的 @DefaultProperties(defaultFallback = "")
註解來配置全域性的服務降級方法,也就是說 自己配置過@HystrixCommand(fallbackMethod = "")
fallbackMethod方法的採用自己配置的服務降級方法,而沒有配置過的就採用@DefaultProperties(defaultFallback = "")
配置的全域性的服務降級方法。這樣的話通用的服務降級方法和獨享的服務降級方法分開,避免了程式碼膨脹,合理減少了程式碼量,修改服務消費方80的Controller入下:
@RestController
@Slf4j
//沒有特別指明就用這個統一的
@DefaultProperties(defaultFallback = "payment_Global_FailHandler")
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
//特別指明使用哪一個兜底方法
// @HystrixCommand(fallbackMethod = "paymentInfo_FailHandler",
// commandProperties = {
// @HystrixProperty(
// name = "execution.isolation.thread.timeoutInMilliseconds",
// value = "1500")
// })
@HystrixCommand //沒有具體指明就使用全域性的
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_FAIL(id);
return result;
}
/** 定製服務降級方法*/
public String paymentInfo_FailHandler(Integer id) {
return "我是消費者80,系統忙,請稍後再試";
}
/**
* 全域性服務降級方法
* @return
*/
public String payment_Global_FailHandler() {
return "全域性異常處理資訊";
}
}
這裡需要注意的是,無論是否配置了定製服務降級方法,都要在其服務上加入註解 @HystrixCommand
, 否則服務降級和該服務沒關係。
而對於第二個問題, 我們可以為Feign客戶端定義的介面新增一個服務降級處理的實現類即可實現解耦,我們的80客戶端已經有了PaymentHystrixService介面,我們新建一個類PaymentFallbackService
實現該介面,並重寫介面中的方法,為介面裡的方法進行異常處理,並且我們在PaymentHystrixService
@FeignClient註解中宣告其服務降級方法所在的類:
@Service
//當出現錯誤是到PaymentFallbackService類中找服務降級方法
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",
fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_FAIL(@PathVariable("id") Integer id);
}
@Service
public class PaymentFallbackService implements PaymentHystrixService{
@Override
public String paymentInfo_OK(Integer id) {
return "PaymentHystrixService paymentInfo_OK出現異常";
}
@Override
public String paymentInfo_FAIL(Integer id) {
return "PaymentHystrixService paymentInfo_Fail出現異常";
}
}
然後我們將Controller中耦合的程式碼都取消:
@RestController
@Slf4j
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_OK(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_FAIL(id);
return result;
}
}
然後我們關閉8001服務提供方服務,模擬伺服器當機,在服務訪問出現錯誤時,訪問了我們配置的PaymentFallbackService類中的服務降級方法,這樣就實現了程式碼的解耦,使業務邏輯不再混亂。
4. Hystrix服務熔斷Break
4.1 熔斷機制概述
服務的降級 => 進而熔斷 => 恢復呼叫鏈路
熔斷機制是應對雪崩效應的一種微服務鏈路保護機制,當扇出鏈路的某個微服務出錯不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的呼叫,也就是說服務熔斷會導致服務降級,快速返回錯誤的響應資訊。當檢測到該節點微服務呼叫響應正常後,恢復呼叫鏈路。也就是說,服務熔斷在服務好了之後會重新允許訪問服務。
在SpringCloud框架中,熔斷機制通過Hystrix實現。Hystrix會監控微服務間的呼叫狀況,當失敗的呼叫到一定閾值,預設是5秒內20次呼叫失敗,就會啟動熔斷機制。熔斷機制的註解是 @HystrixCommand
。關於熔斷機制,具體可以參考論文CircuitBreaker。
4.2 例項
在8003服務提供方的Service中新增如下程式碼:
/**
* 服務熔斷
* fallbackMethod 服務降級方法
* circuitBreaker.enabled 是否開啟斷路器
* circuitBreaker.requestVolumeThreshold 請求次數
* circuitBreaker.sleepWindowInMilliseconds 時間視窗期
* circuitBreaker.errorThresholdPercentage 失敗率達到多少後跳閘
*
* 以下配置意思是在10秒時間內請求10次,如果有6次是失敗的,就觸發熔斷器
*
* 註解@HystrixProperty中的屬性在com.netflix.hystrix.HystrixCommandProperties類中檢視
*/
@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 不能為負數");
}
//hutool工具類的使用,等價於UUID.randomUUID().toString()
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + " 呼叫成功,流水號: " + serialNumber;
}
/**
* 服務熔斷觸發的服務降級方法
* @param id
* @return
*/
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
return "id 不能為負數,請稍後再試。id:" + id;
}
在 @HystrixCommand
註解中配置熔斷機制的引數,配置的引數含義如下:
屬性名 | 含義 | 預設值 |
---|---|---|
circuitBreaker.enabled | 是否開啟斷路器 | true |
circuitBreaker.requestVolumeThreshold | 請求次數 | 20次 |
circuitBreaker.sleepWindowInMilliseconds | 時間視窗期 | 5000ms |
circuitBreaker.errorThresholdPercentage | 失敗率達到多少後跳閘 | 50% |
這些屬性名的具體含義以及其預設值可以在 com.netflix.hystrix.HystrixCommandProperties
類中進行檢視。而我們在service中配置的意思就是在10秒時間內請求10次,如果有6次是失敗的,就觸發熔斷器。
在Controller中新增該服務:
@GetMapping("circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Long id) {
String result = paymentService.paymentCircuitBreaker(id);
log.info("====> result:" + result);
return result;
}
4.3 測試
根據我們的業物邏輯,也就是當我們的id為整數時,服務可以正常訪問,而當id為負數時,訪問服務出錯。我們先訪問 http://localhost:8003/payment/hystrix/circuit/11 代表正確的服務請求,可以發現一切正常!
然後我們進行大量的錯誤訪問,強行觸發服務熔斷,然後在進行正確的訪問。我們發現在進行超出我們閾值的錯誤訪問後,觸發了服務熔斷,即使再進行正確的訪問也無法進行,但是一定時間後,正確的服務訪問又可以順利進行,這就是服務熔斷的整體過程:在觸發了服務熔斷後,先進行服務的降級,再逐漸恢復呼叫鏈路。
4.4 總結
結合官網中對熔斷機制的描述,其熔斷過程可以如下描述:
熔斷器開啟和關閉的精確方式如下:
- 假設電路上的訪問達到某個閾值(
HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()
)… - 並假設誤差百分比超過閾值誤差百分比(
HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()
)… - 然後,斷路器從
CLOSED
為OPEN
,觸發熔斷機制。 - 當它斷開時,它會使針對該斷路器的所有請求短路。
- 經過一段時間(
HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()
)後,下一個單個請求被允許通過(這是HALF-OPEN
狀態)。如果請求失敗,斷路器將OPEN
在睡眠視窗期間返回到該狀態。如果請求成功則斷路器切換到CLOSED
,並且第一條中的邏輯再次接管。
也就是在熔斷機制中,熔斷器分為三個狀態:
狀態 | 說明 |
---|---|
熔斷器開啟OPEN | 請求不再進行呼叫當前服務,內部設定時鐘一般為MTTR(平均故障處理時間),當開啟時長達到所設時鐘則進入半熔斷狀態(HALF-OPEN)。 |
熔斷器關閉CLOSED | 熔斷關閉不會對服務進行熔斷。 |
熔斷器半開HALF-OPEN | 部分請求根據規則呼叫當前服務,如果請求成功且符合規則則認為當前服務恢復正常,關閉熔斷。 |
下面是官網上的熔斷器流程圖:
那麼熔斷器在什麼情況下開始起作用呢?
涉及到熔斷器的三個重要引數:快照時間窗,請求總數閾值,錯誤百分比閾值
-
快照時間視窗期
circuitBreaker.sleepWindowInMilliseconds
:熔斷器是否開啟需要統計一些請求和錯誤資料,而統計的時間範圍就是快找時間窗,預設為最近的10秒; -
請求總數閾值
circuitBreaker.requestVolumeThreshold
:在快照時間窗內,必須滿足請求總數閾值才有資格觸發熔斷,預設為20次,這意味著在快照時間窗規定的時間內,如果該Hystrix命令的呼叫次數不足20次,即使所有請求都超時或其他原因失敗,熔斷器都不會開啟; -
錯誤百分比閾值
circuitBreaker.errorThresholdPercentage
:當請求總數在快照時間窗內超過了閾值,且在這些呼叫中,超過錯誤百分比閾值比例的錯誤呼叫,熔斷器就會開啟。
在熔斷器開啟後
熔斷器開啟後再有請求呼叫的時候,將不會呼叫主邏輯,而是直接呼叫服務降級的方法,實現了自動發現錯誤並將降級邏輯切換為主邏輯,減少響應延遲的效果。
原來的主邏輯如何恢復呢? => 當熔斷器開啟後,對主邏輯進行熔斷之後,Hystrix會啟動一個 休眠時間窗 , 在這個時間窗內,降級邏輯是臨時的主邏輯,當休眠時間窗到期,熔斷器會進入半開狀態,釋放一次請求到原來的主邏輯上,如果此次請求能夠正常訪問,則熔斷器會進入閉合狀態,從而恢復主邏輯,如果註冊請求依然有問題,則熔斷器繼續保持開啟狀態,並且休眠時間窗重新計時。
Hystrix所有常用配置
服務限流在Alibaba的Sentinel框架中會詳解
5. Hystrix工作流程
整體的Hystrix工作流程圖入下:
步驟說明:
6. Hystrix服務監控Hystrix Dashboard
6.1 理解
除了隔離依賴服務的呼叫以外,Hystrix還提供了準實時的呼叫監控——Hystrix Dashboard,Hystrix會持續的記錄所有通過Hystrix發起的請求的執行資訊,並以統計報表和圖形的形式展示給使用者,包括每秒執行多少請求,多少成功,多少失敗等。SpringCloud也提供了Hystrix Dashboard的整合,對監控內容轉化成視覺化介面。
6.2 使用步驟
新建Module:cloud-consumer-hystrix-dashboard9001作為Hystrix Dashboard服務
新增Hystrix Dashboard的依賴:
<dependencies>
<!-- Hystrix Dashboard -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!-- actuator監控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 通用配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
寫配置檔案application.yml,加個埠即可:
server:
port: 9001
編寫主啟動類,在主啟動類上新增@EnableHystrixDashboard
註解開啟Hystrix Dashboard功能:
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class);
}
}
注意:所有的服務提供方微服務(如我們的8001/8002/8003)都需要監控依賴配置:
<!--actuator監控資訊完善-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
訪問 http://localhost:9001/hystrix 我們就可以看見Hystrix Dashboard的圖形化介面
注意:新版本Hystrix需要在主啟動類中指定監控路徑(否則會報錯:Unable to connect to Command Metric Stream)。為了讓服務提供方的服務能被Hystrix Dashboard監控到,需要在提供方服務的主啟動類中新增如下配置:
/**
* 此配置是為了服務監控而配置,與服務容錯本身無關,springcloud升級後的坑
* ServletRegistrationBean因為springboot的預設路徑不是"/hystrix.stream",
* 只要在自己的專案裡配置上下面的servlet就可以了
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
在Hystrix Dashboard的圖形化介面中輸入要監控的服務提供者:
下面就是Dashboard對服務的監控狀態:
如何看?
- 七色
- 一圈 實現圓共有兩種含義。它通過顏色的變化代表了例項的健康程度,它的健康度從綠色<黃色<橙色<紅色遞減。該實心圓除了顏色的變化之外,它的大小也會根據例項的請求流量發生變化,流量越大該實心圓也就越大。所以通過該實心圓的展示,就可以在大量的例項中快速的發現 故障例項和高壓力例項。
- 一線 曲線用來記錄2分鐘內流量的相對變化,可以通過它來觀察到流量的上升和下降趨勢
- 整圖說明1
- 整圖說明2
- 一些複雜的情況