一、Hystrix
1、服務雪崩(遇到的問題)
多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C有調用其他的微服務,這就是所謂的”扇出”,如扇出的鏈路上某個微服務的調用響應式過長或者不可用,對微服務A的調用就會占用越來越多的系統資源,進而引起系統雪崩,所謂的”雪崩效應”。
2、Hystrix
Hystrix是一個用於分布式系統的延遲和容錯的開源庫。在分布式系統里,許多依賴不可避免的調用失敗,比如超時、異常等,Hystrix能夠保證在一個依賴出問題的情況下,不會導致整個服務失敗,避免級聯故障,以提高分布式系統的彈性。
“斷路器”本身是一種開關裝置,當某個服務單元發生故障監控(類似熔斷保險絲),向調用方法返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調用方法無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地占用,從而避免了故障在分布式系統中的蔓延。乃至雪崩。
-
觸發服務降級的場景(什么時候會觸發降級?)
-
程序運行異常
-
超時
-
服務熔斷觸發服務降級
-
線程池、信號量打滿也會導致服務降級
-
-
服務熔斷處理(降級如何處理?)
-
相當於保險絲,直接拉閘,服務的降級-》進而熔斷-》恢復調用鏈路
-
-
服務限流
-
秒殺高並發等操作
-
使用的是Tomcat的內置線程池,所以在高並發下,可能服務會出現線程不夠用的情況
-
接近實時的監控
-
涉及到斷路器的三個重要參數
@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次,即使所有的請求都超時或其他原因失敗,斷路器都不會打開;
-
錯誤百分比閾值:當請求總數在快照時間窗內超過了閾值,比如發生了30次調用,如果在這30次調用中,有15次發生了超時異常,也就是超過了50%的錯誤百分比,在默認設定的50%閾值情況下,這時候就會將斷路器打開。
-
二、服務熔斷
熔斷機制是應對雪崩效應的一種微服務鏈路保護機制,
當扇出鏈路的某個微服務不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的調用,快速返回”錯誤”的響應信息。當檢測到該節點微服務響應正常后恢復調用鏈路,在SpringCloud框架機制通過Hystrix實現,Hystrix會監控微服務見調用的狀況,當失敗的調用到一個閾值,缺省是5秒內20次調用失敗就會啟動熔斷機制,熔斷機制的注解是@HystrixCommand
創建一個工程,命名為:DeptProvider8001_Hystrix_App
1、增加pom.xml中的依賴包
<?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>microservicecloud</artifactId> <groupId>com.yufeng.springcloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>microservicecloud-provider-dept-hystrix-8001</artifactId> <dependencies> <!-- hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <!-- 將微服務provider側注冊進eureka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- 引入自己定義的api通用包,可以使用Dept部門Entity --> <dependency> <groupId>com.yufeng.springcloud</groupId> <artifactId>microservicecloud-api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!-- actuator監控信息完善 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> </dependencies> </project>
2、yml文件修改,修改服務名稱instance-id:為自己的微服務名稱即可
server: port: 8001 mybatis: config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路徑 type-aliases-package: com.yufeng.springcloud.entities # 所有Entity別名類所在包 mapper-locations: - classpath:mybatis/mapper/**/*.xml # mapper映射文件 spring: application: name: microservicecloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource # 當前數據源操作類型 driver-class-name: org.gjt.mm.mysql.Driver # mysql驅動包 url: jdbc:mysql://192.168.172.20:3306/cloudDB01 # 數據庫名稱 username: root password: root dbcp2: min-idle: 5 # 數據庫連接池的最小維持連接數 initial-size: 5 # 初始化連接數 max-total: 5 # 最大連接數 max-wait-millis: 200 # 等待連接獲取的最大超時時間 eureka: client: # 客戶端注冊進eureka內 service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ instance: instance-id: provider-hystrix-dept-8001 prefer-ip-address: true # 訪問路徑可以顯示IP info: app.name: yufeng-microservicecloud company.name: www.yufeng.com build.artifactId: $project.artifactId$ build.version: $project.version$
3、增加熔斷機制(@HystrixCommand)
@RestController public class DeptController { @Autowired private DeptService service; @RequestMapping(value = "/dept/add", method = RequestMethod.POST) public boolean add(@RequestBody Dept dept) { return service.add(dept); } @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
//一旦調用服務方法失敗拋出“錯誤信息后,胡自動范湖用@HystrixCommand標注好的fallbackMethod調用類中的指定方法” @HystrixCommand(fallbackMethod = "processHystrixGet") public Dept get(@PathVariable("id") Long id) { Dept dept = service.get(id); //模擬異常 if(null == dept) { throw new RuntimeException("該id的信息不存在! id=" + id); } return service.get(id); } public Dept processHystrixGet(@PathVariable("id") Long id) { return new Dept().setDname("id沒有對應的信息, null -- @HystrixCommand, id=" + id) .setDb_source("no this database in MySQL."); } }4、主啟動類添加注解 @EnableCircuitBreaker
添加此注解告訴主啟動類對Hystrix的支持
@SpringBootApplication @EnableEurekaClient //本服務啟動后自動注冊到eureka中 @EnableCircuitBreaker //對hystrixR熔斷機制的支持 public class DeptProvider8001_Hystrix_App { public static void main(String[] args) { SpringApplication.run(DeptProvider8001_Hystrix_App.class, args); } }
5、測試:
(1)啟動 3個eureka服務:EurekaServer7001、EurekaServer7002、EurekaServer7003;
(2)啟動 DeptProvider8001_Hystrix_App 服務;
(3)最后啟動DeptConsumer80_App 服務。
(4)測試地址:
http://localhost/consumer/dept/get/2
http://localhost/consumer/dept/get/112
三、服務降級
服務降級的處理是在客戶端完成的,與服務端沒有關系。
整體資源快不夠用了,忍痛將某些服務先關掉,待度過難關,再回來開啟。
所謂降級,就是一般是從整體符合考慮,就是當某個服務熔斷之后,服務器將不再被調用,此刻客戶端可以自己准備一個本地的fallback回調,返回一個缺省值,這樣做,雖然服務水平下降,但好歹可用,比直接掛掉要強。
1、修改消費者服務接口的提供者項目(microservicecloud-api),讓service接口實現一個FallbackFactory接口類DeptClientServiceFallbackFactory(千萬記得增加 @Component 注解);
注意:直接在接口定義的熔斷機制中進行服務熔斷,之前在controller上的@HystrixCommand(fallbackMethod=”methodName”)將棄用
import com.yufeng.springcloud.entities.Dept; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component; import java.util.List; @Component public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> { @Override public DeptClientService create(Throwable throwable) { return new DeptClientService() { @Override public boolean add(Dept dept) { return false; } @Override public Dept get(Long id) { Dept dept = new Dept(); dept.setDeptno(id); dept.setDname("該ID:" + id + "沒有對應的信息,Consumer客戶端提供的信息,此服務Provider已關閉"); dept.setDb_source("no this database in mysql"); return dept; } @Override public List<Dept> list() { return null; } }; } }
2、修改提供服務的service熔斷處理的機制
此處是在公共的service對某個service的方法訪問出現異常后進行統一的fallback處理,即在 DeptClientService接口在注解@FeignClient 中添加 fallbackFactory 屬性值,該屬性賦值為實現FallbcakFactory接口的異常處理類。
import com.yufeng.springcloud.entities.Dept; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.List; @FeignClient(value = "microservicecloud-dept", fallbackFactory = DeptClientServiceFallbackFactory.class) public interface DeptClientService { @RequestMapping(value = "/dept/add", method = RequestMethod.POST) public boolean add(@RequestBody Dept dept); @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET) public Dept get(@PathVariable("id") Long id); @RequestMapping(value = "/dept/get/list", method = RequestMethod.GET) public List<Dept> list(); }
3、在消費者工程(microservicecloud-consumer-dept-feign)的yml文件增加如下內容,開啟feign中的hystrix
feign: hystrix: enabled: true
4、將 microservicecloud-api 工程重新打包,執行: mvn clean install
5、測試
(1)啟動3個 eureka 服務;
(2)啟動 microservicecloud-provider-dept-8001 服務;
(3)啟動 microservicecloud-consumer-dept-feign 服務;
(4)在瀏覽器中訪問:http://localhost/consumer/dept/get/1
(5)將 microservicecloud-provider-dept-8001 服務關閉,瀏覽器訪問:http://localhost/consumer/dept/get/1
四:springCloud服務降級的三種方式
1. 使用Hystrix配置降級,降級方法寫在Controller中,每個方法寫一個
pom導入依賴:
feign調用服務啟用服務降級
在主啟動類啟用hystrix
在調用放配置降級方法
2.降級方法寫在Controller中,寫一個統一的降級方法
controller上加上注解,設置統一默認的降級方法
在每個方法上,加上注解@HystrixCommand