Spring Cloud:服务熔断Hystrix
✨ Spring Cloud:服务熔断Hystrix
- 服务容错的核心知识
- 雪崩效应
- 服务隔离
- 熔断降级
- 服务限流
- Hystrix介绍
- Hystrix案例
- 案例搭建
- 创建新模块cloud-provider-hystrix-payment8001
- 添加依赖
- application.yml
- 主启动类
- Service及其实现类
- Controller
- 正常测试
- 高并发测试
- 使用Jmeter进行测试
- 创建新模块cloud-consumer-feign-hystrix-order80
- 测试
- 测试结论 & 解决方法
- 服务降级
- 服务提供者8001降级
- 服务消费者80降级
- 默认的fallback
- 服务降级处理的实现类
- 服务熔断
- 熔断机制介绍
- 熔断状态
- Hystrix熔断机制的实现
- 代码实现
- image.png
- 服务监控:hystrixDashboard
- 基本介绍
- 搭建 Hystrix Dashboard,监控其他模块的运行情况。
- 新建模块cloud-consumer-hystrix-dashboard9001
- pom依赖
- application.yml
- 主启动类
- 所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置
- 启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001
- 断路器演示
- 修改cloud-provider-hystrix-payment8001
- 监控测试
- 1) 启动1个eureka
- 2) 观察监控窗口
?个人主页:不断前进的皮卡丘
?博客描述:梦想也许遥不可及,但重要的是追梦的过程,用博客记录自己的成长,记录自己一步一步向上攀登的印记
?个人专栏:微服务专栏
服务容错的核心知识
雪崩效应
在微服务架构中,一个请求需要调用多个服务是非常常见的。如客户端访问A服务,而A服务需要调用B 服务,B服务需要调用C服务,由于网络原因或者自身的原因,如果B服务或者C服务不能及时响应,A服务将处于阻塞状态,直到B服务C服务响应。此时若有大量的请求涌入,容器的线程资源会被消耗完毕, 导致服务瘫痪。服务与服务之间的依赖性,故障会传播,造成连锁反应,会对整个微服务系统造成灾难 性的严重后果,这就是服务故障的“雪崩”效应
雪崩是系统中的蝴蝶效应导致其发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方
法响应变慢,亦或是某台机器的资源耗尽。从源头上我们无法完全杜绝雪崩源头的发生,但是雪崩的根
本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。
服务隔离
它是指将系统按照一定的原则划分为若干个服务模块,各个模块之间相对独立,无强依赖。
当有故障发生时,能将问题和影响隔离在某个模块内部,而不扩散风险,不波及其它模块,不影响整体
的系统服务。
熔断降级
熔断和降级的真实关系,图文并茂,看完秒懂
熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。 在互联网 系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整 体的可用性,可以暂时切断对下游服务的调用。这 种牺牲局部,保全整体的措施就叫做熔断.例如你的A服务里面的一个功能依赖B服务,这时候B服务出问题了,返回的很慢。这种情况可能会因为这么一个功能而拖慢了A服务里面的所有功能,因此我们这时候就需要熔断!即当发现A要调用这B时就直接返回错误(或者返回其他默认值啊啥的),就不去请求B了。
降级也就是服务降级,当我们的服务器压力剧增,为了保证核心功能的可用性,可以选择性的降低一些功能的可用性,或者直接关闭该功能。典型的弃车保帅!服务器忙,请稍候再试,不让客户端等待并立刻返回一个友好提示 **就比如贴吧类型的网站,当服务器吃不消的时候,可以选择把发帖功能关闭,注册功能关闭,改密码,改头像这些都关了,为了确保登录和浏览帖子这种核心的功能。
哪些情况会触发降级
1.程序运行异常
2.超时自动降级
3.服务熔断触发服务降级
4.线程池/信号量打满也会导致服务降级
5.人工降级
:::warning
降级一般而言是我们自身的系统出现了故障而降级。而熔断一般是指依赖的外部接口出现故障,断绝和外部接口之间的关联。
:::
服务限流
服务限流
限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说 系统的吞吐量是可以被测算的,为了保证系统的稳固运行,一旦达到的需要限制的阈值,就需要限制流 量并采取少量措施以完成限制流量的目的。比方:推迟解决,拒绝解决,或者者部分拒绝解决等等。
Hystrix介绍
Hystrix是由Netflflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失 败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。
- 包裹请求:使用HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用 了设计模式中的“命令模式”。
- 跳闸机制:当某服务的错误率超过一定的阈值时,Hystrix可以自动或手动跳闸,停止请求该服务 一段时间。
- 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,
发往该依赖的请求就被立即拒绝,而不是排队等待,从而加速失败判定。
监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝 的请求等。
- 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑由开发人员 自行提供,例如返回一个缺省值。
- 自我修复:断路器打开一段时间后,会自动进入“半开”状态。
Hystrix案例
案例搭建
创建新模块cloud-provider-hystrix-payment8001
添加依赖
<?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>cloud2022</artifactId>
<groupId>com.zyh.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-hystrix-payment8001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--之前写的公共模块的坐标-->
<dependency>
<groupId>com.zyh.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<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>
<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>
application.yml
server:
port: 8001
spring:
application:
name: cloud-hystrix-payment-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka/
主启动类
Service及其实现类
Service
package com.zyh.springcloud.service;
/**
* @author zengyihong
* @create 2022--10--16 11:07
*/
public interface PaymentService {
public String paymentInfo_OK(Integer id);
public String payment_Timeout(Integer id);
}
实现类
package com.zyh.springcloud.service.impl;
import com.zyh.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* @author zengyihong
* @create 2022--10--16 11:08
*/
@Service
public class PaymentServiceImpl implements PaymentService {
//成功
@Override
public String paymentInfo_OK(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id: " + id + "\t" + "皮卡皮卡";
}
//失败
@Override
public String payment_Timeout(Integer id) {
int timeNumber = 3;
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (Exception e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id: " + id + "\t" + "呜呜呜" + " 耗时(秒)" + timeNumber;
}
}
Controller
package com.zyh.springcloud.controller;
import com.zyh.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author zengyihong
* @create 2022--10--16 11:17
*/
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentService.paymentInfo_OK(id);
log.info("*******result:" + result + ",端口:" + serverPort);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentService.payment_Timeout(id);
log.info("*******result:" + result + ",端口:" + serverPort);
return result;
}
}
正常测试
- 启动eureka7001
- 启动cloud-provider-hystrix-payment8001
- 访问http://localhost:8001/payment/hystrix/ok/31
每次调用耗费3秒钟:http://localhost:8001/payment/hystrix/timeout/31
上述module均OK
- 以上述为根基平台,从正确->错误->降级熔断->恢复
高并发测试
使用Jmeter进行测试
开启Jmeter,来20000个并发压死8001,20000个请求都去访问payment_Timeout服务
然后需要保存这些配置
压测的过程中再使用postman访问一下微服务
http://localhost:8001/payment/hystrix/ok/31
http://localhost:8001/payment/hystrix/timeout/31
测试的时候,我们会发现响应速度很慢,会一直在转圈,很卡,这是因为tomcat默认的工作线程数被打满了,所以没有多余的线程来分解压力和处理。刚刚测试的时候,我们只是对服务提供者8001进行测试,如果说这个时候服务消费者80也来进行访问,那么消费者就只能一直等待了,最终导致消费者等不及,可能导致超时异常,而且服务端8001会被拖死。
创建新模块cloud-consumer-feign-hystrix-order80
创建新模块cloud-consumer-feign-hystrix-order80
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>cloud2022</artifactId>
<groupId>com.zyh.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-feign-hystrix-order80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--新增hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.zyh.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<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>
<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>
yml
server:
port: 80
spring:
application:
name: cloud-provider-hystrix-payment-service
eureka:
client:
register-with-eureka: true #表识不向注册中心注册自己
fetch-registry: true #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://localhost:7001/eureka/
主启动类
Service
package com.zyh.springcloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author zengyihong
* @create 2022--10--16 14:53
*/
@FeignClient("CLOUD-HYSTRIX-PAYMENT-SERVICE")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String payment_Timeout(@PathVariable("id") Integer id);
}
Controller
package com.zyh.springcloud.controller;
import com.zyh.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author zengyihong
* @create 2022--10--16 14:53
*/
@RestController
@Slf4j
public class OrderHystrixController {
@Autowired
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_OK(id);
log.info("*******result:" + result);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.payment_Timeout(id);
log.info("*******result:" + result);
return result;
}
}
测试
5W个线程压8001
消费端80微服务再去访问正常的OK微服务8001地址
测试结论 & 解决方法
8001同一层次的其他接口服务被困死,因为tomcat线程里面的工作线程已经被挤占完毕
80此时调用8001,客户端访问响应缓慢,转圈圈正因为有上述故障或不佳表现,才有我们的降级/容错/限流等技术诞生
那么要怎么解决呢
服务降级
服务提供者8001降级
我们可用设置自身调用超时时间的峰值,峰值内可以正常运行,否则执行降级方法
一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
由代码可知,为 payment_Timeout方法编写一个回退方法payment_TimeoutHandler,**payment_TimeoutHandler方法与 payment_Timeout方法具有相同的参数与返回值类型,该方法返回一个默认的错误信息。 **
在 payment_Timeout方法上,使用注解@HystrixCommand的fallbackMethod属性,指定熔断触发的降级方法 是payment_TimeoutHandler 。
- 因为熔断的降级逻辑方法必须跟正常逻辑方法保证:相同的参数列表和返回值声明。
在 payment_Timeout 方法上 HystrixCommand(fallbackMethod = “findProductFallBack”) 用来声明一个降级逻辑的方法
package com.zyh.springcloud.service.impl;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.zyh.springcloud.service.PaymentService;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;
/**
- @author zengyihong
@create 2022—10—16 11:08
*/
@Service
public class PaymentServiceImpl implements PaymentService {//成功
@Override
public String paymentInfo_OK(Integer id) {return "线程池:" + Thread.currentThread().getName() + " paymentInfo_OK,id: " + id + "\t" + "皮卡皮卡";
}
//超时降级演示
@HystrixCommand(fallbackMethod = “payment_TimeoutHandler”, commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000") //5秒钟以内就是正常的业务逻辑
})
//失败
@Override
public String payment_Timeout(Integer id) {
int timeNumber = 13;
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (Exception e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id: " + id + "\t" + "呜呜呜" + " 耗时(秒)" + timeNumber;
}
/**
* 兜底方法,上面方法出问题的话,代替业务方法来返回兜底结果,返回一个出错信息
* 降级方法的声明要求:必须是public,返回结果类型要一致。
* 参数类型,参数个数一致,方法名自定义
* @param id
* @return
*/
public String payment_TimeoutHandler(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " payment_TimeoutHandler,系统繁忙,请稍后再试8001 ";
}
}
主启动类激活
@EnableCircuitBreaker
服务消费者80降级
服务降级可以在服务提供者侧,也可以在服务消费者侧。更多是在服务消费者侧。
yml
feign:
hystrix:
enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
在主启动类加上@EnableHystrix
Controller
package com.zyh.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.zyh.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author zengyihong
* @create 2022--10--16 14:53
*/
@RestController
@Slf4j
public class OrderHystrixController {
@Autowired
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_OK(id);
log.info("*******result:" + result);
return result;
}
@HystrixCommand( fallbackMethod = "payment_TimeoutHandler",commandProperties = {
//超过1.5秒就降级自己
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.payment_Timeout(id);
log.info("*******result:" + result);
return result;
}
/**
* 兜底方法,上面方法出现问题,就来执行这个方法,返回一个出错信息
* @param id
* @return
*/
public String payment_TimeoutHandler(Integer id){
return "我是消费者80,对方支付系统繁忙,或者自己运行出错,请检查自己";
}
}
测试
默认的fallback
我们刚才把fallback写在了某个业务方法上,如果这样的方法很多,那岂不是要写很多。所以我们可以
把Fallback配置加在类上,实现默认fallback。
package com.zyh.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.zyh.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zengyihong
* @create 2022--10--16 14:53
*/
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "globalHandler")
public class OrderHystrixController {
@Autowired
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id) {
String result = paymentHystrixService.paymentInfo_OK(id);
log.info("*******result:" + result);
return result;
}
// @HystrixCommand( fallbackMethod = "payment_TimeoutHandler",commandProperties = {
// //超过1.5秒就降级自己
// @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
// })
@HystrixCommand
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
String result = paymentHystrixService.payment_Timeout(id);
log.info("*******result:" + result);
return result;
}
/**
* 兜底方法,上面方法出现问题,就来执行这个方法,返回一个出错信息
* @param
* @return
*/
// public String payment_TimeoutHandler(Integer id){
// return "我是消费者80,对方支付系统繁忙,或者自己运行出错,请检查自己";
// }
public String globalHandler(){
return "我是全局降级处理方法";
}
}
服务降级处理的实现类
本次案例服务降级处理是在客户端80实现完成的,与服务端8001没有关系,只需要为Feign客户端定义的接口添加一个服务降级处理的实现类即可实现解耦
根据cloud-consumer-feign-hystrix-order80已经有的PaymentHystrixService接口,重新新建一个类(PaymentFallbackService)实现该接口,统一为接口里面的方法进行异常处理。
仅仅只有这个实现类还不行,我们还得告诉service,我们为他准备了一个服务降级的实现类
测试
服务熔断
熔断机制介绍
- 熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长的时候,为了保护系统的整体可用性,熔断器会暂时切断请求对该服务的调用,并快速返回一个友好的错误响应。这种熔断状态不是永久的,在经历了一定的时间后,熔断器会再次检测该微服务是否恢复正常,若服务恢复正常则恢复其调用链路。
- 当检测到该节点微服务调用响应正常以后,恢复调用链路。
- 在Spring Cloud框架中,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状态,当失败的调用到一定阙值的时候,默认是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand
- https://martinfowler.com/bliki/CircuitBreaker.html (大神级别论文)
熔断状态
在熔断机制中涉及了三种熔断状态:
- 熔断关闭状态(Closed):当服务访问正常时,熔断器处于关闭状态,服务调用方可以正常地对服务进行调用。
- 熔断开启状态(Open):默认情况下,在固定时间内接口调用出错比率达到一个阈值(例如 50%),熔断器会进入熔断开启状态。进入熔断状态后,后续对该服务的调用都会被切断,熔断器会执行本地的降级(FallBack)方法。
- 半熔断状态(Half-Open): 在熔断开启一段时间之后,熔断器会进入半熔断状态。在半熔断状态下,熔断器会尝试恢复服务调用方对服务的调用,允许部分请求调用该服务,并监控其调用成功率。如果成功率达到预期,则说明服务已恢复正常,熔断器进入关闭状态;如果成功率仍旧很低,则重新进入熔断开启状态。
Hystrix熔断机制的实现
在 Spring Cloud 中,熔断机制是通过 Hystrix 实现的。Hystrix 会监控微服务间调用的状况,当失败调用到一定比例时(例如 5 秒内失败 20 次),就会启动熔断机制。
Hystrix 实现服务熔断的步骤如下:
- 当服务的调用出错率达到或超过 Hystix 规定的比率(默认为 50%)后,熔断器进入熔断开启状态。
- 熔断器进入熔断开启状态后,Hystrix 会启动一个休眠时间窗,在这个时间窗内,该服务的降级逻辑会临时充当业务主逻辑,而原来的业务主逻辑不可用。
- 当有请求再次调用该服务时,会直接调用降级逻辑快速地返回失败响应,以避免系统雪崩。
- 当休眠时间窗到期后,Hystrix 会进入半熔断转态,允许部分请求对服务原来的主业务逻辑进行调用,并监控其调用成功率。
- 如果调用成功率达到预期,则说明服务已恢复正常,Hystrix 进入熔断关闭状态,服务原来的主业务逻辑恢复;否则 Hystrix 重新进入熔断开启状态,休眠时间窗口重新计时,继续重复第 2 到第 5 步。
代码实现
- ** **修改cloud-provider-hystrix-payment8001中的PaymentService,添加一个public String paymentCircuitBreaker(Integer id)方法;
在 PaymentService 接口的实现类 PaymentServiceImpl 添加 paymentCircuitBreaker() 的方法实现及其回退方法
//服务熔断
@Override
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //当在配置时间窗口内(10s)达到此数量(10个)的失败后,打开断路器,默认是20个失败的请求
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //断路多久以后开始尝试是否恢复,默认5s
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //出错百分比阈值,当达到此阈值后,开始短路。默认50%
})
public String paymentCircuitBreaker(Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();//hutool.cn工具包
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
" class="reference-link">
- 在 PaymentController 中添加一个 paymentCircuitBreaker() 方法对外提供服务
- 测试
服务监控:hystrixDashboard
基本介绍
除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。Netflix通过hystrix-metrics-event-stram项目实现了对以上指示的监控。Spring Cloud也提供了Hystrix Dashboard的整合,对监控内容转化成可视化界面。
搭建 Hystrix Dashboard,监控其他模块的运行情况。
新建模块cloud-consumer-hystrix-dashboard9001
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>cloud2022</artifactId>
<groupId>com.zyh.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--新增hystrix dashboard-->
<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>
application.yml
server:
port: 9001 #端口号
hystrix:
dashboard:
proxy-stream-allow-list: "*"
主启动类
所有Provider微服务提供类(8001/8002/8003)都需要监控依赖配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
启动cloud-consumer-hystrix-dashboard9001该微服务后续将监控微服务8001
断路器演示
修改cloud-provider-hystrix-payment8001
注意:新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径
package com.zyh.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.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
/**
* @author zengyihong
* @create 2022--10--16 11:05
*/
@SpringBootApplication
@EnableEurekaClient
/**
* 开启熔断功能
*/
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.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;
}
}
监控测试
1) 启动1个eureka
2) 观察监控窗口
9001监控8001
http://localhost:9001/hystrix
http://localhost:8001/hystrix.stream
发送一次请求
然后将以下信息填到 Hystrix 监控页面中
还没有评论,来说两句吧...