-
- 1. Hystrix了解
-
- 1.1 概述
- 1.2 背景
- 1.3 雪崩效應常見場景
- 1.4 雪崩效應應對政策
- 1.5 初探Hystrix
- 1.6 Hystrix流程圖
- 2. 建構問題項目
-
- 2.2 建構cloud-provider-hystrix-payment8007項目
-
- 2.2.1 pom檔案
- 2.2.2 yml檔案
- 2.2.3 啟動類
- 2.2.4 控制器
- 2.2.5 普通測試
- 2.2.6 高并發測試
- 2.3 建構cloud-consumer-hystrix-order6007項目
-
- 2.3.1 pom檔案
- 2.3.2 yml檔案
- 2.3.3 啟動類
- 2.3.4 Feign的Service接口
- 2.3.5 控制器
- 2.3.6 普通測試
- 3.2.7 對8007高并發測試
- 3 服務降級
-
- 3.1 概述
- 3.2 服務提供者8007服務降級(逾時)
-
- 3.2.1 啟動類
- 3.2.2 控制器類
- 3.2.3 測試
- 3.3 服務消費者6007服務降級(異常)
-
- 3.3.1 啟動類
- 3.3.2 控制器
- 3.3.3 測試
- 3.4 簡化代碼結構
-
- 3.4.1 預設Fallback方法
- 3.4.2 OpenFeign與Hystrix合用
- 4. 服務熔斷
-
- 4.1 概述
- 4.1.1 熔斷器是什麼
-
- 4.1.2 熔斷器狀态
- 4.1.3 執行過程
- 4.2 案例示範
-
- 4.2.1 HystrixController
- 4.2.2 測試
- 4.2.3 熔斷器全部配置
- 5. 服務監控HystrixDashboard
-
- 5.1 建立cloud-hystrix-dashboard9001子產品
-
- 5.1.1 pom檔案
- 5.1.2 yml檔案
- 5.1.3 啟動類
- 5.2 修改被監控項目cloud-provider-hystrix-payment8007
-
- 5.2.1 pom檔案添加actuator依賴
- 5.2.2 啟動類
- 5.3 通路監控頁面
易于了解筆記:https://www.cnblogs.com/yawen/p/6655352.html
詳細概念流程:https://blog.csdn.net/loushuiyifan/article/details/82702522
1. Hystrix了解
1.1 概述
Hystrix是一個用于處理分布式系統的延遲和容錯的開源庫,在分布式系統裡,許多依賴不可避免的會調用失敗,比如逾時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導緻整體服務失敗,避免級聯故障,以提高分布式系統的彈性。
“斷路器”本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向調用方傳回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者抛出調用方無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地占用,進而避免了故障在分布式系統中的蔓延,乃至雪崩。
Hystrix作用:
- 服務降級
- 服務熔斷
- 服務限流
- 實時監控
1.2 背景
分布式系統環境下,服務間類似依賴非常常見,一個業務調用通常依賴多個基礎服務。如下圖,對于同步調用,當庫存服務不可用時,商品服務請求線程被阻塞,當有大批量請求調用庫存服務時,最終可能導緻整個商品服務資源耗盡,無法繼續對外提供服務。并且這種不可用可能沿請求調用鍊向上傳遞,這種現象被稱為雪崩效應。

1.3 雪崩效應常見場景
- 硬體故障:如伺服器當機,機房斷電,光纖被挖斷等。
- 流量激增:如異常流量,重試加大流量等。
- 緩存穿透:一般發生在應用重新開機,所有緩存失效時,以及短時間内大量緩存失效時。大量的緩存不命中,使請求直擊後端服務,造成服務提供者超負荷運作,引起服務不可用。
- 程式BUG:如程式邏輯導緻記憶體洩漏,JVM長時間FullGC等。
- 同步等待:服務間采用同步調用模式,同步等待造成的資源耗盡。
1.4 雪崩效應應對政策
針對造成雪崩效應的不同場景,可以使用不同的應對政策,沒有一種通用所有場景的政策,參考如下:
- 硬體故障:多機房容災、異地多活等。
- 流量激增:服務自動擴容、流量控制(限流、關閉重試)等。
- 緩存穿透:緩存預加載、緩存異步加載等。
- 程式BUG:修改程式bug、及時釋放資源等。
- 同步等待:資源隔離、MQ解耦、不可用服務調用快速失敗等。資源隔離通常指不同服務調用采用不同的線程池;不可用服務調用快速失敗一般通過熔斷器模式結合逾時機制實作。
綜上所述,如果一個應用不能對來自依賴的故障進行隔離,那該應用本身就處在被拖垮的風險中。 是以,為了建構穩定、可靠的分布式系統,我們的服務應當具有自我保護能力,當依賴服務不可用時,目前服務啟動自我保護功能,進而避免發生雪崩效應。本文将重點介紹使用Hystrix解決同步等待的雪崩問題。
1.5 初探Hystrix
Hystrix [hɪst’rɪks],中文含義是豪豬,因其背上長滿棘刺,進而擁有了自我保護的能力。本文所說的Hystrix是Netflix開源的一款容錯架構,同樣具有自我保護能力。為了實作容錯和自我保護,下面我們看看Hystrix如何設計和實作的。
Hystrix設計目标:
- 對來自依賴的延遲和故障進行防護和控制——這些- - 依賴通常都是通過網絡通路的
- 阻止故障的連鎖反應
- 快速失敗并迅速恢複
- 回退并優雅降級
- 提供近實時的監控與告警
Hystrix遵循的設計原則:
- 防止任何單獨的依賴耗盡資源(線程)
- 過載立即切斷并快速失敗,防止排隊
- 盡可能提供回退以保護使用者免受故障
- 使用隔離技術(例如隔闆,泳道和斷路器模式)來限制任何一個依賴的影響
- 通過近實時的名額,監控和告警,確定故障被及時發現
- 通過動态修改配置屬性,確定故障及時恢複
- 防止整個依賴用戶端執行失敗,而不僅僅是網絡通信
Hystrix如何實作這些設計目标?
- 使用指令模式将所有對外部服務(或依賴關系)的調用包裝在HystrixCommand或HystrixObservableCommand對象中,并将該對象放在單獨的線程中執行;
- 每個依賴都維護着一個線程池(或信号量),線程池被耗盡則拒絕請求(而不是讓請求排隊)。
- 記錄請求成功,失敗,逾時和線程拒絕。
- 服務錯誤百分比超過了門檻值,熔斷器開關自動打開,一段時間内停止對該服務的所有請求。
- 請求失敗,被拒絕,逾時或熔斷時執行降級邏輯。
- 近實時地監控名額和配置的修改。
1.6 Hystrix流程圖
2. 建構問題項目
2.2 建構cloud-provider-hystrix-payment8007項目
2.2.1 pom檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-hystrix-payment8007</artifactId>
<dependencies>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency><!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
<groupId>com.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>
</project>
2.2.2 yml檔案
server:
port: 8007
spring:
application:
name: cloud-provider-hystrix-payment
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
2.2.3 啟動類
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8007 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8007.class,args);
}
}
2.2.4 控制器
package com.springcloud.controller;
import org.omg.CORBA.TIMEOUT;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
public class PaymentController {
@GetMapping("payment/hystrix/ok")
public String ok(){
return "線程池:"+Thread.currentThread().getName()+",資訊:OK";
}
@GetMapping("payment/hystrix/timeout")
public String timeout(){
long timeout=3000;
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "線程池:"+Thread.currentThread().getName()+",資訊:TIMEOUT ";
}
}
2.2.5 普通測試
- 啟動7001
- 啟動8007
- 通路http://localhost:8007/payment/hystrix/ok
- 通路http://localhost:8007/payment/hystrix/timeout
【SpringCloud】Hystrix斷路器【五】 【SpringCloud】Hystrix斷路器【五】
2.2.6 高并發測試
- 對http://localhost:8007/payment/hystrix/ok進行高并發
- 通路ok頁面(卡頓)
- 通路timeout頁面(卡頓)
兩個頁面都卡頓的原因是并發量超過了Tomcat的工作線程數,沒有多餘的線程來分解壓力和處理。
2.3 建構cloud-consumer-hystrix-order6007項目
2.3.1 pom檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-hystrix-order6007</artifactId>
<dependencies>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency><!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
<groupId>com.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</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>
</project>
2.3.2 yml檔案
server:
port: 6007
spring:
application:
name: cloud-consumer-hystrix-order
eureka:
client:
register-with-eureka: false
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
2.3.3 啟動類
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain6007 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain6007.class,args);
}
}
2.3.4 Feign的Service接口
package com.springcloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentFeignService {
@GetMapping("/payment/hystrix/ok")
public String ok();
@GetMapping("/payment/hystrix/timeout")
public String timeout();
}
2.3.5 控制器
package com.springcloud.controller;
import com.springcloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
PaymentFeignService paymentFeignService;
@GetMapping("/consumer/payment/hystrix/ok")
public String ok(){
return paymentFeignService.ok();
}
@GetMapping("/consumer/payment/hystrix/timeout")
public String timeout(){
return paymentFeignService.timeout();
}
}
2.3.6 普通測試
- 啟動7001
- 啟動8007
- 啟動6007
- 通路http://localhost:6007/consumer/payment/hystrix/ok
- 通路http://localhost:6007/consumer/payment/hystrix/timeout
3.2.7 對8007高并發測試
繼續對8007高并發測試,因為6007調用了8007的服務,但是8007因為被大量并發夾住了,導緻6007也卡住了,最終導緻使用者體驗不好
3 服務降級
3.1 概述
當服務出現異常狀況時不讓用戶端等待并立刻傳回一個友好提示fallback
哪些情況會觸發降級
- 程式運作異常
- 逾時
- 服務熔斷觸發服務降級
- 線程池/信号量打滿也會導緻服務降級
以8007和6007作為例子
- 對方服務(8007)逾時或down機了,調用者(6007)不能一直卡死等待,必須有服務降級
- 對方服務(8007)OK,調用者(6007)自己出故障或有自我要求(自己的等待時間小于服務提供者),自己處理降級
3.2 服務提供者8007服務降級(逾時)
3.2.1 啟動類
增加@EnableCircuitBreaker或@EnableHystrix注解,@EnableHystrix繼承擴充了@EnableCircuitBreaker的功能
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8007 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8007.class,args);
}
}
3.2.2 控制器類
使用@HystrixCommand注解添加到需要服務降級的方法上
package com.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.omg.CORBA.TIMEOUT;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
public class PaymentController {
@GetMapping("/payment/hystrix/ok")
public String ok(){
String result="線程池:"+Thread.currentThread().getName()+",資訊:OK";
log.info(result);
return result;
}
// fallbackMethod是當timeout()出錯或逾時時調用timeout_handler()進行處理響應給調用者
@HystrixCommand(fallbackMethod = "timeout_handler",commandProperties ={
// execution.isolation.thread.timeoutInMilliseconds表示timeout()執行超過指定時間則觸發服務降級
// 因為6007的Feign預設隻會等待1秒,是以這裡設定900毫秒,有足夠時間把timeout_handler()響應給6007
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "900")
})
@GetMapping("/payment/hystrix/timeout")
public String timeout(){
long timeout=3000;
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
String result="線程池:"+Thread.currentThread().getName()+",資訊:TIMEOUT";
log.info(result);
return result;
}
public String timeout_handler(){
String result="線程池:"+Thread.currentThread().getName()+",資訊:調用8007支付接口逾時或異常 ";
return result;
}
}
3.2.3 測試
- 啟動7001
- 啟動8007
- 啟動6007
- 注意:修改@HystrixCommand中的屬性需要重新開機服務,自動部署devtools不一定生效
- 通路http://localhost:6007/consumer/payment/hystrix/timeout
3.3 服務消費者6007服務降級(異常)
3.3.1 啟動類
增加@EnableCircuitBreaker或@EnableHystrix注解,@EnableHystrix繼承擴充了@EnableCircuitBreaker的功能
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class PaymentHystrixMain8007 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8007.class,args);
}
}
3.3.2 控制器
package com.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.springcloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
PaymentFeignService paymentFeignService;
@HystrixCommand(fallbackMethod = "ok_handler")
@GetMapping("/consumer/payment/hystrix/ok")
public String ok(){
int num=10/0;
return paymentFeignService.ok();
}
public String ok_handler(){
return "6007出錯了,請聯系管理者";
}
@GetMapping("/consumer/payment/hystrix/timeout")
public String timeout(){
return paymentFeignService.timeout();
}
}
3.3.3 測試
- 啟動7001
- 啟動8007
- 啟動6007
- 注意:修改@HystrixCommand中的屬性需要重新開機服務,自動部署devtools不一定生效
- 通路http://localhost:6007/consumer/payment/hystrix/ok
3.4 簡化代碼結構
3.4.1 預設Fallback方法
package com.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.springcloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
// 指定預設的Fallback方法
@DefaultProperties(defaultFallback = "default_handler")
public class OrderController {
@Autowired
PaymentFeignService paymentFeignService;
// 預設Fallback必須是無參的
public String default_handler(){
return "8007系統down機啦啦啦啦啦啦啦啦啦";
}
@HystrixCommand(fallbackMethod = "ok_handler")
@GetMapping("/consumer/payment/hystrix/ok")
public String ok(){
int num=10/0;
return paymentFeignService.ok();
}
public String ok_handler(){
return "6007出錯了,請聯系管理者";
}
// @HystrixCommand如果沒有設定fallbackMethod屬性,則會自動使用預設Fallback
@HystrixCommand
@GetMapping("/consumer/payment/hystrix/timeout")
public String timeout(){
return paymentFeignService.timeout();
}
}
啟動7001
啟動6007
不要啟動8007
通路http://localhost:6007/consumer/payment/hystrix/ok
通路http://localhost:6007/consumer/payment/hystrix/timeout
3.4.2 OpenFeign與Hystrix合用
- yml檔案
server:
port: 6007
spring:
application:
name: cloud-consumer-hystrix-order
eureka:
client:
register-with-eureka: false
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka
# OpenFeign與Hystrix何用,需要添加以下代碼
feign:
hystrix:
enabled: true
- 啟動類
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients // 開啟OpenFeign支援
@EnableHystrix // 開啟Hystrix支援
public class OrderHystrixMain6007 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain6007.class,args);
}
}
- Feign服務接口
設定@FeignClient的fallback屬性
package com.springcloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
@Component
// 在@FeignClient注解的fallback屬性是為目前調用遠端服務的Feign接口進行服務降級Fallback處理的類
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentFeignService {
@GetMapping("/payment/hystrix/ok")
public String ok();
@GetMapping("/payment/hystrix/timeout")
public String timeout();
}
- Fallback處理類
package com.springcloud.service;
import org.springframework.stereotype.Component;
@Component //記得加上@Component注解
// 實作Feign服務的接口,為每個遠端通路的方法設定Fallback
public class PaymentFallbackService implements PaymentFeignService {
@Override
public String ok() {
return "8007的ok出錯了";
}
@Override
public String timeout() {
return "8007的timeout出錯了";
}
}
- 控制器
還原成原來的樣子
package com.springcloud.controller;
import com.springcloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
PaymentFeignService paymentFeignService;
@GetMapping("/consumer/payment/hystrix/ok")
public String ok(){
return paymentFeignService.ok();
}
@GetMapping("/consumer/payment/hystrix/timeout")
public String timeout(){
return paymentFeignService.timeout();
}
}
- 測試
啟動7001
啟動6007
不要啟動8007
通路http://localhost:6007/consumer/payment/hystrix/ok
通路http://localhost:6007/consumer/payment/hystrix/timeout
4. 服務熔斷
4.1 概述
4.1.1 熔斷器是什麼
熔斷機制是應對雪崩效應的一種微服務鍊路保護機制,當扇對外連結路的某個微服務出錯不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的調用,快速傳回錯誤的響應資訊。當檢測到該節點微服務調用響應正常後,恢複調用鍊路
4.1.2 熔斷器狀态
閉合:不會對服務進行熔斷,即不幹活
斷開:請求不再進行調用目前服務,直接fallback
半開:斷開後每隔一段時間,嘗試調用目前服務,根據規則改變熔斷器狀态
4.1.3 執行過程
- 當請求數滿足一定的閥值的時候(預設10秒内超過20個請求次數)熔斷器才會開始工作;如果請求數低于閥值,熔斷器不會幹活,即上面流程完全不執行。
- 熔斷器一開始是閉合狀态,如果一定時間内超過規定請求失敗率(預設10秒内50%的失敗率)熔斷器就會斷開;如果低于失敗率則正常通路目前服務。
- 熔斷器斷開期間(預設5秒)所有請求直接進入fallback。
- 熔斷器斷開指定時間(預設5秒)後會進入半開狀态,會讓其中一個請求通路目前服務,如果成功則把熔斷器閉合,如果失敗則把熔斷器斷開。
- 根據熔斷器狀态重複以上步驟
4.2 案例示範
為了省事在8007建立一個HystrixController,直接在浏覽器通路HystrixController中的方法,不再用6007調用8007的服務
4.2.1 HystrixController
package com.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HystrixController {
@HystrixCommand(fallbackMethod = "division_handler",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //啟用熔斷器
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //快照時間窗
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"), //在快照時間窗内請求總數閥值
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"),//在快照時間窗内請求失敗百分比閥值
})
@GetMapping("/hystrix/division/{num}")
public String division(@PathVariable("num") int num){
int result=10/num;
return "結果:"+result;
}
public String division_handler(@PathVariable("num") int num){
return "出錯啦,數字:"+num;
}
}
4.2.2 測試
單次通路![]()
【SpringCloud】Hystrix斷路器【五】 ![]()
【SpringCloud】Hystrix斷路器【五】
過程:先10秒内錯誤通路5次,10秒後正确通路1次
結果:10秒後通路那一次正确顯示結果
原因:10秒内通路5次,未達到讓熔斷器幹活的程度
過程:先10秒内錯誤通路20次以上,第10秒正确通路1次,第15秒正确通路1次,第20秒正确通路1次
結果:10秒後通路那一次顯示出錯了,第15秒通路顯示成功,第20秒通路成功
原因:10秒内通路超過20次,熔斷器開始幹活了,因為在這10秒内全是錯誤通路,失敗率達到了100%,遠超過規定的50%,是以熔斷器斷開了,斷開 的期間(預設持續5秒),所有請求都是直接調用fallback,是以雖然是正确通路,但結果是錯誤的;熔斷器斷開5秒後進入半開狀态,嘗試恢複鍊路,對第15秒通路目前服務,通路成功了;第15秒通路成功會把熔斷器從半開切換成閉合狀态,是以第20秒也能正常通路(未到熔斷器幹活次數)
4.2.3 熔斷器全部配置
//========================All
@HystrixCommand(fallbackMethod = "str_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"),
}
)
public String strConsumer() {
return "hello 2020";
}
public String str_fallbackMethod()
{
return "*****fall back str_fallbackMethod";
}
5. 服務監控HystrixDashboard
5.1 建立cloud-hystrix-dashboard9001子產品
5.1.1 pom檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-hystrix-dashboard9001</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<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>
</project>
5.1.2 yml檔案
server:
port: 9001
5.1.3 啟動類
package com.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}
5.2 修改被監控項目cloud-provider-hystrix-payment8007
5.2.1 pom檔案添加actuator依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
5.2.2 啟動類
package com.springcloud;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableEurekaClient
@EnableHystrix
public class PaymentHystrixMain8007 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8007.class,args);
}
/**
*此配置是為了服務監控而配置,與服務容錯本身無關,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;
}
}