Hystrix特性
1.断路器机制
断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.
2.Fallback
Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.
3.资源隔离
在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池. 例如调用产品服务的Command放入A线程池, 调用账户服务的Command放入B线程池. 这样做的主要优点是运行环境被隔离开了. 这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响. 但是带来的代价就是维护多个线程池会对系统带来额外的性能开销. 如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用Hystrix的信号模式(Semaphores)来隔离资源.
1.pesservice01项目的pom.xml引入Hyxtrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
2.写一个简单的demo TestHystrixCommand.java
继承HystrixCommand类 重写 run() getFallback() 方法接口
package com.wying.pesservice01.HystrixLocalDemo;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
/**
* description:spring cloud熔断器 demo
* 继承HystrixCommand类 重写 run() getFallback() 方法
* date: 2021/12/27
* author: gaom
* version: 1.0
*/
public class TestHystrixCommand extends HystrixCommand<String> {
protected TestHystrixCommand(HystrixCommandGroupKey group) {
super(group,4000);
}
@Override
protected String run() throws Exception {
System.out.println("运行run begen");
Thread.sleep(5000);
System.out.println("运行run end");
return "执行完毕!";
}
@Override
protected String getFallback() {
//return super.getFallback();
return "超时熔断了...";
}
}
写个main方法执行测试 HystrixTest.java
package com.wying.pesservice01.HystrixLocalDemo;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
/**
* description:spring cloud熔断器 运行 测试效果
* date: 2021/12/27
* author: gaom
* version: 1.0
*/
public class HystrixTest {
public static void main(String[] args){
HystrixCommand<String> hystrixCommand=new TestHystrixCommand(HystrixCommandGroupKey.Factory.asKey("testGroup"));
String rtn= hystrixCommand.execute();
System.out.println(rtn);
}
}
在TestHystrixCommand.java的构造器中设置的超时时间4S, super(group,4000);
而run方法中休眠了5S,超过4后进入熔断器,执行了 getFallback()
3.原理分析
在上面的demo中,发现熔断器的使用很简单,直接 extends HystrixCommand 重写run方法, getFallback方法就实现了熔断器的功能。
但是很好奇
(1)熔断时间达到4S触发熔断是怎么监听到的
(2)Thread.sleep(5000) 触发熔断条件后,是怎么回调getFallback()方法的?
(3)是通过什么技术中断后面的代码执行,熔断后控制台没输出 "运行run end"字符串 说明主线程后面的代码都没运行了
能想到的是熔断器原理肯定不是一个单线程可以实现的,主线程运行run()方法的同时应该开启了一个另外的线程来监听熔断超时时间,中断掉主线程执行,以及回调getFallback()方法
查资料得到的是想要了解Hystrix原理首先需要了解rxjava Java响应式(反应式)编程。
RxJava本质上是一个异步操作库,Hystrix的熔断机制也是通过rxjava实现的。
看了下pom依赖结构,发现引入spring-cloud-starter-netflix-hystrix.jar后会依赖引入rxjava.jar
4. 先学习rxjava去了。。。
之前没接触过rxjava,查资料看了一个demo,也看不大懂
https://mp.csdn.net/mp_blog/creation/editor/122436073
5. pesservice01项目启用hystrix熔断器
03)章节 实现了 pesservice01 通过feign 调用pesservice02 http服务
5.1 pesservice01,perservice02 都引入hystrix依赖
我这边先把pesservice01作为服务调用方,pesservice02作为服务提供方配置熔断功能
这个时候pesservice02不引入hystrix依赖也行,只有服务调用方需要引入hystrix依赖,我这边pesservice01,perservice02后面存在服务互调,每个项目即是服务提供方,也是服务调用方
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.9.RELEASE</version>
</dependency>
5.2 pesservice01项目服务调用方yml配置文件 feign节点增加配置,启用hystrix
application.yml
server:
#服务端口号
port: 8012
spring:
application:
# 服务名称 - 服务之间使用名称进行通讯
name: pes-service-01
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
cloud:
circuitbreaker:
hystrix:
enabled: true
eureka:
client:
service-url:
# 填写注册中心服务器地址
defaultZone: http://127.0.0.1:7012/eureka
# 是否需要将自己注册到注册中心
register-with-eureka: true
# 是否需要搜索服务信息
fetch-registry: true
instance:
# 使用ip地址注册到注册中心
prefer-ip-address: true
# 注册中心列表中显示的状态参数
instance-id: ${spring.cloud.client.ip-address}:${server.port}
# Timeout单位为ms 1000即为1S
feign:
client:
config:
default:
#请求连接超时时间
connectTimeout: 5000
#请求处理超时时间
readTimeout: 5000
5.3 pesservice01项目服务调用方Pesservice01Application.java增加@EnableHystrix注解
Pesservice01Application.java
package com.wying.pesservice01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
/**
* @EnableHystrix - 开启断路器
*/
@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
public class Pesservice01Application {
public static void main(String[] args) {
SpringApplication.run(Pesservice01Application.class, args);
}
}
5.4 PesTestController01.java的test03方法新增注解@HystrixCommand(fallbackMethod = "test03Fallback")
编写test03Fallback()方法
5.5 先看下没配置hystrix熔断器之前接口调用超时的效果
也就是 03)章节 pesservice01 通过feign 调用pesservice02 http服务超时的效果
5.6 hystrix熔断器生效后调用超时的效果
访问pesrvice01项目的test03服务, pesService02FeginClient.test01(reqMap); 调用pesservice02的服务时,由于pesservice02的test01服务设置了sleep 睡眠时间,导致超时,之前是报错fegin 调用超时,然后返回postman springboot默认的异常信息
现在的效果是回调test03Fallback方法,后台也没报错了,我们可以在test03Fallback方法做一些操作。
6.hystrix熔断器生效条件(代码抛出异常/执行时间大于hyxtrix超时时间)
首先 @HystrixCommand(fallbackMethod = "test03Fallback")注解是配置在pesservice01项目的test03()方法上。只要test03方法代码执行中有任何报错,异常,或者调用某服务超时导致抛出异常
都会触发熔断。(有异常就会触发熔断)
6.1 比如代码中加入一句Integer.parseInt("s");手动制造一个异常
postman测试
熔断起作用进入回调方法了
进入回调方法
6.2 配置熔断器忽略的异常ignoreExceptions={NumberFormatException.class}
因为默认是任何异常都熔断,如果想忽略部分异常可以通过ignoreExceptions属性配置
配置后Integer.parseInt("s");抛出NumberFormatException也不会触发熔断条件,返回了springboot默认的报错
6.3 熔断器默认的超时时间,及自定义超时时间
抛出异常,错误在没配置忽略的情况下肯定直接进入fallback回调方法,另外如果tets03执行速度过慢也会触发熔断。要搞清楚的是这里feign配置的超时和hystrix的超时没直接关系。
是2个配置。hystrix有自己的超时时间。Hystrix默认超时时间是1秒。
我们在test03方法 执行 Thread.sleep(1000);肯定就触发熔断条件了,其他地方也要微量的时间,肯定大于1S,触发熔断条件。大部分情况1S肯定是不能执行完的。需要自定义hystrix的超时时间。
自定义超时时间也是可以在yml配置全局的,也可以在注解上配置,肯定是优先使用注解的。
yml全局配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
测试效果,hystrix配置的超时时间为3S
睡眠4S时postman报错 进入熔断,睡眠2S则不会熔断
注解上单独配置
如果这个方法执行4S是正常现象,可以@HystrixCommand注解单独配置
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000" )
}
7.hystrix触发熔断后fallback回调方法中获取入参和异常信息
上面测试的代码执行抛出异常熔断或者代码执行时间超过hystrix超时时间熔断都是直接回调fallback方法,由于控制台没报错了,我们都不知道为什么触发了熔断条件。哪个异常导致的。
test03Fallback回调方法入参除了test03的入参外,额外增加一个Throwable t入参
这样我们在回调方法就能获取方法的入参,和异常了,可以根据instanc判断异常类型做不同的降级处理以及记录日志等。
public Map<String, Object> test03Fallback(HashMap<String,Object> reqMap,Throwable t) {
String errorMsg="PesService01 使用fegin客户端 调用PesService02 /pesTestController02/test01 服务失败了,触发熔断器 执行 PesTestController01 test03Fallback()方法";
System.out.println(errorMsg);
System.out.println("入参:"+reqMap.toString());
System.out.println("触发熔断的原因:"+t.toString());
//打印错误栈信息
t.printStackTrace();
Map<String, Object> resMap = new HashMap<>();
resMap.put($CODE, $301);
resMap.put($MESSAGE,errorMsg );
return resMap;
}
代码执行超时触发熔断导致的异常
空指针导致的异常