目录
一、Hystrix概述
Hystrix:中文:豪猪,因其背上长满棘刺,从而拥有了自我保护的能力。
服务雪崩:
1、服务雪崩介绍:
分布式系统面临的问题:服务雪崩
分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图:
如果各个服务正常运行,那大家齐乐融融,高高兴兴的,但是如果其中一个服务崩坏掉会出现什么样的情况呢?
上图中:当Service A的流量波动很大,流量经常会突然性增加!那么在这种情况下,就算Service A能扛得住请求,Service B和Service C未必能扛得住这突发的请求。比如这时候Service C由于请求访问过多变得不可用了。
此时,假如Service C因为抗不住请求,变得不可用。那么Service B的请求也会阻塞,慢慢耗尽Service B的线程资源,Service B就会变得不可用。紧接着,Service A也会不可用。
总结:即服务C的不可用,导致原先可用的服务B慢慢也变得不可用了,再然后,就是服务B的不可用也慢慢导致 原本正常的服务A变得不可用,就好像一条串联电路中,一个断路导致全部不能正常工作一样。。
简单地讲。一个服务失败,导致整条链路的服务都失败的情形,我们称之为服务雪崩。
2、引起雪崩的原因和服务雪崩的三个阶段
原因大致有四:
1、硬件故障;
2、程序Bug;
3、缓存击穿(用户大量访问缓存中没有的键值,导致大量请求查询数据库,使数据库压力过大);
4、用户大量请求;
服务雪崩的三个阶段:
第一阶段: 服务不可用;
第二阶段:调用端重试加大流量(用户重试/代码逻辑重试);
第三阶段:服务调用者不可用(同步等待造成的资源耗尽);
3、解决服务雪崩的方案
1) 应用扩容(扩大服务器承受力)
加机器
升级硬件
2)流量控制(超出限定流量,返回类似重试页面让用户稍后再试)
限流
关闭重试
3) 缓存
将用户可能访问的数据大量的放入缓存中,减少访问数据库的请求。
4)服务降级
服务接口拒绝服务
页面拒绝服务
延迟持久化
随机拒绝服务
5) 服务熔断
Hystrix简介:
官网资料:https://github.com/Netflix/Hystrix/wiki/How-To-Use
Hystrix是一个用于处理分布式系统的延迟和容错的开源库。在分布式系统中,许多依赖不可避免的额会调用失败,比如超时,异常等,Hystrix能保证在一个依赖出现问题的情况下,不会导致整体的服务失败,避免级联故障,以提高分布式系统的稳定性。
Hystrix是用作断路器的,断路器本省是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方 返回一个符合预期的、可处理的备选响应/应急方案(fallback),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而避免了故障在分布式系统中蔓延,乃至服务雪崩。
fallback:服务降级/应急方案
官网资料:https://github.com/Netflix/Hystrix/wiki/How-To-Use
停更维护:https://github.com/Netflix/Hystrix
二、Hystrix重要概念:
fallback:服务降级/应急方案
服务熔断:
当下游的服务因为某种原因突然变得不可用或响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用
服务降级
服务降级主要有两种场景:
一、当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度!
二、当下游的服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回给用户!
已经看出来,第一个场景怎么和服务熔断的概念有异曲同工之妙,没错,服务熔断可视为降级方式的一种!
结合上述场景,在实际的项目中,采用以下的方式来完成降级工作
- 梳理出核心业务流程和非核心业务流程。然后在非核心业务流程上加上开关,一旦发现系统扛不住,关掉开关,结束这些次要流程。
- 一个微服务下肯定有很多功能,那自己区分出主要功能和次要功能。然后次要功能加上开关,需要降级的时候,把次要功能关了吧!
- 降低一致性了,即将核心业务流程的同步改异步,将强一致性改最终一致性。
Hystrix是Netflix公司开源的一款容错框架。 它可以完成以下几件事情:
-
资源隔离,包括线程池隔离和信号量隔离,避免某个依赖出现问题会影响到其他依赖。
-
断路器,当请求失败率达到一定的阈值时,会打开断路器开关,直接拒绝后续的请求,并且具有弹性机制,在后端服务恢复后,会自动关闭断路器开关。
-
降级回退,当断路器开关被打开,服务调用超时/异常,或者资源不足(线程、信号量)会进入指定的fallback降级方法。
-
请求结果缓存,hystrix实现了一个内部缓存机制,可以将请求结果进行缓存,那么对于相同的请求则会直接走缓存而不用请求后端服务。
-
请求合并, 可以实现将一段时间内的请求合并,然后只对后端服务发送一次请求。
Hystrix如何实现:
- 使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行;
- 每个依赖都维护着一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)
- 记录请求成功,失败,超时和线程拒绝。
- 服务错误百分比超过了阈值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。
- 请求失败,被拒绝,超时或熔断时执行降级逻辑。
- 近实时地监控指标和配置的修改。
三、Hystrix案例
准备工作:
构建:
(1)创建提供者payment模块:cloud-provider-hystrix-payment8001
(2)pom:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--eureka 服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--hystrix 服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.2.RELEASE</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>
</dependencies>
(3)提供者的application.yml:
(4)主启动类:com.fan.springcloud.PaymentHystrixMain8001
package com.fan.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
(5)业务类:
service类: service.PaymentService
package com.fan.springcloud.service;
import jdk.internal.org.objectweb.asm.commons.TryCatchBlockSorter;
import sun.nio.ch.DefaultSelectorProvider;
import java.util.concurrent.TimeUnit;
@Service
public class PaymentService {
public String paymentInfo_ok(Integer id) //正常的一个方法
{
return "线程池:"+Thread.currentThread().getName()+"paymentinfo_ok,id: "+id+
"\t"+"哈哈ok";
}
public String paymentinfo_timeout(Integer id ){//耗时的一个方法
try {
TimeUnit.SECONDS.sleep(3);
}catch(InterruptedException e){e.printStackTrace();}
return "线程池:" +Thread.currentThread().getName()+
"paymentinfo_timeout,id"+id+"\t"+"哈哈,耗时3秒";
}
}
提供者的contrller:controller.PaymentController
package com.fan.springcloud.controller;
import com.fan.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;
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;//服务类的方法 被注入
@Value("${server.port}")//获取application.yml中的端口号
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);
return result;
}
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_timeout(@PathVariable("id") Integer id){
String result = paymentService.paymentinfo_timeout(id);
log.info("***result:"+result);
return result;
}
}
(6)测试:
启动Eureka注册中心的微服务和提供者的微服务:
浏览器测试:eureka7001.com:7001
测试不耗时的方法:localhost:8001/payment/hystrix/ok/31
测试耗时方法:localhost:8001/payment/hystrix/timeout/31
高并发测试:
以上是在非高并发情况下的测试:为了了解使用hystrix,我们在搞并发的情况下测试:
这里我们使用jmeter工具,jmeter工具的安装和使用请百度。
请求地址:http://localhost:8001/payment/hystrix/timeout/31
然后浏览器先测试:http://localhost:8001/payment/hystrix/timeout/31
然后立马启动另一个不耗时的方法测试:http://localhost:8001/payment/hystrix/ok/31
此时发现,在非高并发情况下,后测试的非耗时的方法并不会卡一下,及等待一下,但是在高并发情况下,我们后测试的不耗时的方法这时候会明显的等待一下(转圈)。
这是因为耗时的操作先运行,占满了tomcat的线程总数,导致后调用的原先瞬时非耗时方法也开始卡顿。
高并发测试消费者和提供者的微服务:
(1)新建消费者模块:cloud-consumer-feign-hystrix-order80
(2)消费者pom:
<dependencies>
<!--注册中心放到第一个,eureka 服务-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--客户端使用openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</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>
<version>2.2.2.RELEASE</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>
</dependencies>
(3)消费者的application.yml:
server:
port: 80
spring:
application:
name: cloud-consumer-hystrix
eureka:
client:
register-with-eureka: true #false表示不向注册中心注册自己。
fetch-registry: true #是否抓取注册信息。
service-url:
defaultZone: http://eureka7003.com:7003/eureka #单机版只写自己本身
(4)主启动类:com.fan.springcloud.OrderHystrixMain80
package com.fan.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
(5)业务类:
5.1 service配合feign的接口:远程调用主要体现在这里:消费者的service接口对接提供者的controller映射方法。
消费者的service.PaymentHystrixService 接口:
package com.fan.springcloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.concurrent.TimeUnit;
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT") //ureka上的那个name service,即application
public interface PaymentHystrixService {
//payment远程能提供的有哪些服务,我们直接去提供者controller那里复制粘贴即可
@GetMapping("/payment/hystrix/ok/{id}")//远程调用接口的定义
public String paymentInfo_ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")//远程调用接口的定义
public String paymentinfo_timeout(@PathVariable("id") Integer id );
}
5.2 消费者的controller: controller.OrderHystrixController
此controller是我们的order端调用payment端,使用的是feign 接口:
package com.fan.springcloud.controller;
import com.fan.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
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;
@RestController
@Slf4j
public class OrderHystrixController {
//注入调用的接口
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")//远程的有参调用,consumer前缀代表来自消费侧
public String paymentInfo_ok(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_ok(id);
return result;
}
@GetMapping("/consumer/payment/hystrix/timeout/{id}")//远程的有参调用,consumer前缀代表来自消费侧
public String paymentInfo_timeout(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_timeout(id);
return result;
}
}
梳理调用线路:消费者controller --调用–> 消费者service --调用–> 提供者controller
启动测试:启动三个服务,依次启动ureka,服务的提供者,服务的消费者
自测8001:http://localhost:8001/payment/hystrix/ok/31
自测8001:http://localhost:8001/payment/hystrix/timeout/31
测试消费者feign接口端:
http://localhost/consumer/payment/hystrix/ok/31
高并发测试:消费端出现等待
服务降级使用:
提供者和消费者的微服务卡死的情况和解决方案:
提供者8001服务降级处理:
此注解@HystrixCommand相当于一个异常处理的注解:
(1)从提供者8001先进行服务降级的处理:PaymentService进行修改
自定义一个应急方案的方法:paymentInfo_TimeOutHandler,相当于一个异常处理方法。
@HystrixCommand报异常后如何处理:
一旦调用服务方法失败并抛出错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法。
package com.fan.springcloud.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class PaymentService {
public String paymentInfo_ok(Integer id)
{
return "线程池:"+Thread.currentThread().getName()+"paymentinfo_ok,id: "+id+
"\t"+"哈哈ok";
}
//降级处理的注解
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentinfo_timeout(Integer id ){
try {
TimeUnit.SECONDS.sleep(4);
}catch(InterruptedException e){e.printStackTrace();}
return "线程池:" +Thread.currentThread().getName()+
"paymentinfo_timeout,id"+id+"\t"+"哈哈,耗时4秒";
}
//出错的应急方法,增加异常处理方法:paymentInfo_TimeOutHandler
public String paymentInfo_TimeOutHandler( Integer id){
log.info("8001降级处理,超时");
return "调用支付接口超时或异常:\t "+Thread.currentThread().getName()+"paymentInfo_TimeOutHandler,id: "+id+
"\t"+"对不起,出错了!!!";
}
}
(2)主启动类添加新注解@EnableCircuitBreaker:
启动注册中心和提供者8001,并测试:
http://localhost:8001/payment/hystrix/ok/31
http://localhost:8001/payment/hystrix/timeout/31
当我们把时间改成4秒的时候,测试:http://localhost:8001/payment/hystrix/timeout/31
就可以测试应急方法是否被调用了: TimeUnit.SECONDS.sleep(4);
当我们把时间改成2秒的时候,测试:http://localhost:8001/payment/hystrix/timeout/31
就没有达到预定的峰值3秒,就走正常的方法。
同样,我们也可以进行程序出错的降级演示:
消费者80进行服务降级处理:
pom不需要改动
这里修改消费者80的yml:
server:
port: 80
spring:
application:
name: cloud-consumer-hystrix
eureka:
client:
register-with-eureka: true #false表示不向注册中心注册自己。
fetch-registry: true #是否抓取注册信息。
service-url:
defaultZone: http://eureka7003.com:7003/eureka #单机版只写自己本身
feign:
hystrix:
enabled: true
主启动类增加注解:@EnableHystrix
修改消费者80的controller中的超时方法:
package com.fan.springcloud.controller;
import com.fan.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
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;
@RestController
@Slf4j
public class OrderHystrixController {
//注入调用的接口
@Resource
private PaymentHystrixService paymentHystrixService;
@GetMapping("/consumer/payment/hystrix/ok/{id}")//远程的有参调用,consumer前缀代表来自消费侧
public String paymentInfo_ok(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_ok(id);
return result;
}
//可能会出错的方法,并指定出错后的应急处理方法
@GetMapping("/consumer/payment/hystrix/timeout/{id}")//远程的有参调用,consumer前缀代表来自消费侧
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "1500")
})
public String paymentInfo_timeout(@PathVariable("id") Integer id){
String result = paymentHystrixService.paymentInfo_timeout(id);
return result;
}
//增加应急处理方法:paymentTimeOutFallbackMethod
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
log.info("我是消费者80的服务降级处理方法");
return "我是消费者80的服务降级处理方法,对方支付系统繁忙,请10秒后再试";
}
}
测试消费者:http://localhost/consumer/payment/hystrix/timeout/31
到此我们就完美解决了服务降级。
服务降级处理的优化:
一种做法是:通常使用是在controller方法上启用注解 @HystrixCommand 但是每个方法都写 fallbackMethod 总有些麻烦 所以可以定义一个抽象类 BaseController 使用的时候 controller 继承 BaseController 即可实现统一 fallbackMethod,类似于异常处理类
初步 的解决方案:
最终 的解决方案:
上述方式还不够极简,比如fallback函数与函数耦合在一起,可以进一步解耦。通过实现@FeignClient的接口,指明fallback类。其中每个函数对应的就是fallback函数。
(1)此实现类PaymentFallbackService 相当于异常处理类,实现了PaymentHystrixService: 还要把此类放入到springboot容器中
@Service
public class PaymentFallbackService implements PaymentHystrixService{
@Override
public String paymentInfoOk(Integer id) {
return "fall back, ok...";
}
@Override
public String paymentInfoTimeout(Integer id) {
return "fall back, timeout...";
}
}
(2)在@FeignClient的接口上,指明fallback类。这样便可实现,fallback函数与函数的进一步解耦。
package com.fan.springcloud.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.concurrent.TimeUnit;
//FeignClient接口,提供访问调用的接口
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class) //ureka上的那个name service,即application
public interface PaymentHystrixService {
//payment远程能提供的有哪些服务,我们直接去提供者controller那里复制粘贴即可
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_ok(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_timeout(@PathVariable("id") Integer id);
}
测试:
正常访问测试: http://localhost/consumer/payment/hystrix/ok/31
故意关闭微服务8001后测试:
http://localhost/consumer/payment/hystrix/ok/31
http://localhost/consumer/payment/hystrix/timeout/31
总结:
服务熔断:
服务熔断概述:
类比保险丝,分为三个步骤:服务降级->进而熔断->恢复调用链路。
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
SpringCloud中,熔断机制是通过Hystrix实现。Hystrix会监控微服务间的调用情况,当失败的调用到一定阈值,默认是5s内20次调用失败,就会启动熔断机制。熔断机制的注解是通过@HystrixCommand实现的。
熔断类型:
- 熔断打开:请求不在进行调用当前服务,内部设置时钟一般为MTTR(平均故障处理时间),当打开时长达到所设时钟则进入半熔断状态。
- 熔断关闭:正常情况。
- 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常,关闭熔断。
涉及断路器的三个重要参数:快照时间窗,请求总数阈值,错误百分比阈值。
快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,统计的时间范围就是快照时间窗,默认是10s。
请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断。默认是20,意味着10s内,如果Hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因,断路器都不会打开。
错误百分比阈值:当请求总数在快照时间窗内超过了阈值,比如发生了30次调用,如果再30次调用中,有16次发送了超时异常,则超过了50%的错误百分比,在默认设定50%阈值的情况下,这时候会将断路器打开。
原来的主逻辑要如何恢复?
当开启的时候,所有请求都不会进行转发;一段时间之后(默认是5s),这个时候断路器是半开状态,会让其中一个请求进行转发,如果成功,断路器会关闭,若失败,继续开启。重复上述步骤。
参考资料:
half open是半开状态
服务熔断案例:
断路器的三个重要参数:快照时间窗、请求总数阀值、错误百分比阀值。
- 快照时间窗:断路器确定是否打开需要统计一 些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。
- 请求总数阀值:在快照时间窗内,必须满足请求总数阀值才有资格熔断。默认为20, 意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
- 错误百分比阀值:当请求总数在快照时间窗内超过了阀值,比如发生了30次调用,如果在这30次调用中,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阀值情况下,这时候就会将断路器打开。
增加提供者8001的PaymentService的方法:
//服务熔断
@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;
}
扩展知识:hutool工具包
了解工具hutool.all工具包:官网地址:hutool.cn
文档地址:https://hutool.cn/docs/#/
UUID全称通用唯一识别码(universally unique identifier),JDK通过java.util.UUID提供了 Leach-Salz 变体的封装。在Hutool中,生成一个UUID字符串方法如下:
//生成的UUID是带-的字符串,类似于:a5c8a5e8-df2b-4706-bea4-08d0939410e3
String uuid = IdUtil.randomUUID();
//生成的是不带-的字符串,类似于:b17f24ff026d40949c85a24f4f375d42
String simpleUUID = IdUtil.simpleUUID();
增加8001的PaymentController的方法:
//服务熔断
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id)
{
String result = paymentService.paymentCircuitBreaker(id);
log.info("****result: "+result);
return result;
}
测试: localhost:8001/payment/circuit/2
测试: localhost:8001/payment/circuit/-2
小总结:
Hystrix工作流程
官网图例:
服务监控HystrixDashbord
创建模块:
pom:
yml:
启动测试:
填写监控地址: