一. 简单设置熔断
服务熔断: 设置一个限制,当服务请求超过限制时,拒绝请求,调用服务降级方法给用户提示信息, ,比如最多只能同时访问100个请求,超出请求放入缓存队列中,队列已满时,拒绝访问,并且调用链路会有一个自动恢复过程,在恢复期内请求正常了可能还是会走降级方法,要等到关闭熔断
示例代码: 设置 fusing()方法10秒内是个请求如果与60%发送错误开启熔断执行降级方法 fallBack2().
// @HystrixProperty 注解中 10, 10000, 60 的解释,10000表示10秒钟内,有10次请求,如果60%报错了,断路器开启
@GetMapping(value = "/fusing/{id}")
@HystrixCommand(fallbackMethod ="fallBack2", commandProperties = {
@HystrixProperty(name="circuitBreaker.enabled", value = "true"), //是否开启断路器
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold", value="20"),//请求次数
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds", value="10000"),//时间窗口期
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage", value = "50")//失败率达到多少跳闸
})
public JsonResult fusing(@PathVariable("id") int id){
System.out.println("开始执行");
int i = 1/id;
return JsonResult.successResult("正常执行");
}
//降级方法
public JsonResult fallBack2(int id){
System.out.println("降级方法开始执行");
return JsonResult.successResult("降级方法执行,接收参数为:"+id);
}
二. 通过编码方式实现熔断降级
- 需要设置熔断降级执行的方法
public class TestService{
//假设该方法需要熔断降级处理,该方法返回 JsonResult 类型数据
public JsonResult myMethod(){
System.out.println("需要熔断降级的实际方法");
return JsonResult.successResult("执行成功");
}
}
- 创建继承 HystrixCommand <熔断方法执行返回的数据类型>类的子类,该类中聚合存有熔断执行的方法的类,重写父类 run()方法,在run()方法中调用需要熔断降级的方法,
import com.common.result.JsonResult;
import com.netflix.hystrix.*;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderHystrixCommand extends HystrixCommand<JsonResult> {
//1.持有保存了需要设置熔断降级方法的类
@Autowired
private TestService testService;
//2.提供带参构造器,创建当前类对象时必须对 TestService 赋值
public OrderHystrixCommand(TestService testService) {
//3.在创建该对象时首先执行 setter() 方法,设置线程池,或信号量
//获取 Setter 赋值给父类,也就是设置熔断机制
super(setter());
this.testService = testService;
}
//4. 配置熔断机制 线程池或信号量方式
//线程池方式原理:
//1.在执行需要熔断降级的方法时,首先会针对服务创建一个线程池
//2.配置线程池的线程个数,线程存活时间,线程列队的个数
//3.当请求该服务的线程超过,该线程池的规定时执行降级方法
private static Setter setter() {
// 服务分组(注意多个服务分组不能重名,一般对应服务)
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("order");
// 服务标识(一般与服务接口对应)
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("order");
// 线程池名称
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order-pool");
// 配置服务熔断规则,配置线程池,超过时执行拒绝策略
HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter()
.withCoreSize(10) //线程池大小为10
.withKeepAliveTimeMinutes(15) //线程存活时间15秒
.withQueueSizeRejectionThreshold(100); //队列等待的阈值为100
// 命令属性配置Hystrix 开启超时
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
// 配置服务隔离方式,采用线程池方式实现服务隔离
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
// 禁止(默认开启超时机制,需要false禁用)
.withExecutionTimeoutEnabled(false);
return HystrixCommand.Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey)
.andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);
}
//通过计数器(信号量)方式,设置服务熔断,与上线程池方式保留一个即可,
//原理:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行
//当请求进来时先判断计数器的数值,若超过设置的最大线程个数则拒绝该请求
//若不超过则通行,这时候计数器+1,请求返回成功后计数器-1。
private static Setter setter2() {
// 服务分组
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("members");
// 命令属性配置 采用信号量模式(计数器方式)
HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
.withExecutionIsolationSemaphoreMaxConcurrentRequests(50); //设置最大进程数,超过则拒绝执行降级方法
return HystrixCommand.Setter.withGroupKey(groupKey).andCommandPropertiesDefaults(commandProperties);
}
//5.重写父类run()方法,在该方法中调用执行需要熔断降级的方法
//可以简单理解为将原来调用的需要熔断降级的方法使用run方法再次包裹一次
//而该方法的实际执行是通过当前类调用父类的 execute()方法
@Override
protected JsonResult run() throws Exception {
JsonResult result = testService.myMethod();
return result;
}
//6.重写getFallback()方法,该方法为降级方法,当对服务的请求超过线程池规定 执行拒绝策略,执行该方法
@Override
protected JsonResult getFallback() {
System.out.println("系统错误!");
return JsonResult.errorResult("熔断方法执行");
}
}
- 通过此处的 myRunTest() 方法接收请求,通过继承了HystrixCommand 的子类 OrderHystrixCommand 调用 execute() 执行 TestService 中需要熔断降级的方法 myMethod()
public JsonResult myRunTest() {
TestService service = new TestService()
return new OrderHystrixCommand( service ).execute();
}
三. 通过编码了解 Hystrix 熔断降级的执行步骤
-
首先会创建一个持有需要熔断降级方法的 HystrixCommand 对象
-
在创建该对象时,会初始化熔断规则,线程池或信号量,获取到一个代表熔断规则的 Setter
-
后续通过 Setter 判断是否需要熔断执行降级方法
-
HystrixCommand 执行 execute()方法,执行实际的业务接口
-
检查是否开启缓存
-
检查是否开启了断路器,如果开启,直接执行降级方法
-
检查线程池/队列/semaphore是否已经满了,是否不会触发熔断降级机制,如果触发直接执行降级方法
-
调用 run() 方法,执行需要熔断降级的方法,返回执行结果
-
如果 run() 方法在执行时抛出异常,或返回超时,也会执行降级方法 getFallback()
-
线程池方式与信号量方式默认容量是10
-
每次调用实际接口成功,超时,失败,拒绝等情况,都会发送给circuit breaker断路器进行统计,后续根据统计结果判断是否需要开启短路等功能
-
触发fallback的情况: run()或construct()执行实际接口发生异常,发送超时,或者hystrix线程池中的线程都在运行中,semaphore队列存满时,或者断路器开启时
hystrix执行实际业务接口的方式
1. HystrixCommand 对象调用 execute() 方法,同步等待业务接口执行完毕后返回单条结果
2. 调用 queue() 方法拿到一个Future,属于异步调用,后续通过Future获取单挑=条执行结果
3. 调用 observe() 订阅一个Observable对象,Obesrvable代表一个接口的返回接口,获取一个接口代表结果的Observable对象的拷贝对象
4. toObservable()
线程池方式熔断降级的原理
1. 在执行需要熔断降级的方法时,首先会针对服务创建一个线程池
2. 配置线程池的线程个数,线程存活时间,线程列队的个数
3. 当请求该服务的线程超过,该线程池的规定时执行降级方法
信号量方式降级的原理
1. 使用一个原子计数器(或信号量)来记录当前有多少个线程在运行
2. 当请求进来时先判断计数器的数值,若超过设置的最大线程个数则拒绝该请求
3. 若不超过则通行,这时候计数器+1,请求返回成功后计数器-1。
线程池方式与信号量方式的不同
1. 线程池方式: tomcat线程接收用户请求后,通过hystrix隔离线程池里的线程执行,从而保证tmcat线程不会因为依赖服务接口的延迟造成其它请求的产生延时等待的问题,当hystrix线程池存满时,直接走fallback
2. 信号量方式: 通过信号量设置并发数量,信号量方式还是使用的tomcat线程,
3. 适用场景: 通常涉及到网络请求的有超时问题的大部分使用线程池方式进行隔离,信号量方式一般用在项目内部服务之间相互的访问设置,不涉及网络请求,避免内部的复杂业务代码导致大量的线程延时阻塞问题
断路器执行流程
断路器默认时间窗口为10秒,运行最大的并发为20个请求,异常比例默认为50%,也就是时间窗口内请求数量达到20个,异常比例大于50%时开启短路功能,在调用接口直接执行fallback降级,经过一段时间后默认5秒,断路器变为half-open半开状态,会尝试让一个请求正常执行,如果执行成功则关闭断路器