SpringCloud Hystrix
引言
微服务中有一个服务响应过慢会拖垮整个服务,所以出现了熔断:
熔断器:
- 请求过大自动熔断,请求超时也会熔断
- 不正常工作也有自己的默认值,保证服务之间的解耦合
Hystrix是一个延迟和容错库,旨在隔离对远程系统,服务和第三方库的访问点,停止级联故障,并在复杂的分布式系统中实现弹性,在这些系统中,故障是不可避免的。
简单写一个熔断
public class MyBreaker {
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
//sum(1,2);
Future<Integer> future = threadPool.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return sum(1, 2);
}
});
Integer returnValue =null;
try {
returnValue =future.get(100, TimeUnit.MILLISECONDS);
} catch (Exception e){
returnValue = 0;
}
System.out.println(returnValue);
}
public static int sum(int x,int y){
try {
int seconds=new Random().nextInt(200);
Thread.sleep(seconds);
System.out.println("sleep:"+seconds);
} catch (InterruptedException e) {
}
return x+y;
}
}
熔断器的代码应该放在spring的环绕的通知里面 Spring的AOP,就是代理对象,切面切出来的结果
Hystrix的库被封装到了spring当中
可以加许多地方dao,service都能加
专门要搞Hystrix的话 参考GitHub上的这个案例
https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica
https://github.com/Netflix/Hystrix/wiki/Configuration
如何集成熔断器
pom.xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
SpringBootApplication入口中需要添加@EnableCircuitBreaker
注解,此时Spring工厂会启动AOP方式对所有的方法上有@HystrixCommand
的业务方法添加熔断策略。
@SpringBootApplication
@EnableCircuitBreaker
@MapperScans(value = {@MapperScan(basePackages = {"com.jiangzz.dao"})})
public class HystrixSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixSpringBootApplication.class,args);
}
}
在业务方法上添加@HystrixCommand
注解实现熔断
//HystrixCommand会产生一个代理类
@HystrixCommand//这个也不是spring提供的是Hystrix的
@Override
public Integer sum(Integer a,Integer b) {
System.out.println(Thread.currentThread().getId());
return a+b;
}
这个时候你在去测试
//这个时候你去查看你的ThreadID 发现变了
@SpringBootTest(classes = HystrixSpringBootApplication.class)
@RunWith(SpringRunner.class)
public class UserServiceTests {
@Autowired
private IUserService userService;
@Test
public void testSum(){
System.out.println("thread:"+Thread.currentThread().getId());
System.out.println(userService.getClass());
Integer result = userService.sum(1, 2);
System.out.println(result);
}
}
线程隔离
默认该方法的执行会启动新的线程执行和主程序不在一个线程中,因此如果上下文中存在ThreadLocal变量,在该方法中就失效了。因此一般可以通过设置commandProperties
注解属性,设置线程就可以了。
//默认的execution.isolation.strategy是thread(做限流必须用这个)但这不能传递ThreadLocal变量
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy",value = "SEMAPHORE")
})
@Override
public Integer sum(Integer a,Integer b) {
System.out.println(Thread.currentThread().getId());
return a+b;
}
Fallback
过在@HystrixCommand中声明fallbackMethod的名称可以实现优雅降级(出错的备选方案),如下所示:
@HystrixCommand(fallbackMethod = "fallbackMethodQueryUserById")
@Override
public Integer sum(Integer a,Integer b) {
System.out.println(Thread.currentThread().getId());
int i=10/0;
return (a+b)*2;
}
//注意这个Throwable必须写在后面
public Integer sum(Integer a,Integer b,Throwable e) {
System.out.println(e.getMessage());
System.out.println(Thread.currentThread().getId());
return a+b;
}
注意要求fallbackMethod方法和目标方法必须在同一个类中,具有相同的参数(异常参数可选)
Error Propagation
根据此描述,@ HystrixCommand能够指定应忽略的异常类型。如下所述ArithmeticException: / by zero将不会触发fallbackMethod方法。
//忽略算数异常
@HystrixCommand(fallbackMethod = "fallbackMethodQueryUserById",ignoreExceptions = {ArithmeticException.class})
@Override
public Integer sum(Integer a,Integer b) {
System.out.println(Thread.currentThread().getId());
int i=10/0;
return (a+b)*2;
}
//注意这个Throwable必须写在后面
public Integer sum(Integer a,Integer b,Throwable e) {
System.out.println(e.getMessage());
System.out.println(Thread.currentThread().getId());
return a+b;
}
请求超时熔断
//默认的execution.isolation.strategy是thread(做限流必须用这个)但这不能传递ThreadLocal变量
@HystrixCommand(commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="100")
})
@Override
public Integer sum(Integer a,Integer b) {
System.out.println(Thread.currentThread().getId());
return a+b;
}
熔断器限流:
断路器:当你每次等100ms才熔断的时候我们可以配置一个次数 如果你连续多少次都熔断了 我就预判你故障了 下次访问的时候我就直接打开你的断路器的开关,每次失败的时候才去熔断然后执行Fallback就慢了。
具体可以参考这个博客:
https://blog.csdn.net/tongtong_use/article/details/78611225
hystrix dashbord(检测)
引入pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<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.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<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-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
propeties
#开启健康检查的所有端口
management.endpoints.web.exposure.include=*
SpringBoot入口类添加@EnableHystrixDashboard注解如下
@SpringBootApplication
@EnableCircuitBreaker
@EnableHystrixDashboard
public class SpringBootApplicationDemo {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationDemo.class,args);
}
}
访问的web端口
先访问
然后访问htttp://ip:port/actuor/hystrix.stream
错误率达到某个阈值 熔断器就应该打开
全开-半开(看你后面能不能正常返回,如果不能再全开)-全闭
配置窗口的大小 要求时间窗口必须是numbucket的整数倍(像spark一样)不然除不整
metrics.rollingStats.timeInMilliseconds
窗口区间个数(可以推出窗口间隔就是窗口大小/区间个数)
metrics.rollingStats.numBuckets
设置最小请求次数
circuitBreaker.requestVolumeThreshold
默认值为20
设置错误率默认值为50%
circuitBreaker.errorThresholdPercentage
设置下一步是全开还是半开的间隔 默认为5秒 半开->全开|闭合 取决于下次请求是否降级
circuitBreaker.sleepWindowInMilliseconds
除了这个之外还可以配置线程池的参数
设置线程池大小
coreSize
默认值为10
设置队列最大等待请求数 这俩个配置了之后 20个以后你的请求就会被限流 表现为服务就必须降级了
queueSizeRejectionThreshold
@Service
public class UserService implements IUserService {
public User failback4QueryUserById(Integer id) {
return new User(-1,"服务降级!");
}
@HystrixCommand(
fallbackMethod = "failback4QueryUserById",
commandProperties = {
@HystrixProperty(name="execution.isolation.strategy",value="THREAD"),
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1000"),
// 时间窗口 必须是 numBuckets的整数倍数
@HystrixProperty(name="metrics.rollingStats.timeInMilliseconds",value = "10000"),//设置窗口长度10秒
@HystrixProperty(name="metrics.rollingStats.numBuckets",value = "10"),//设置窗口滑动间隔1s
//闸刀开启的条件 请求数> 2 && 错误率> 50%
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "20"),//设置最小请求次数
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50"),//设置错误率50%
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"),// 当全开到半开的时间间隔
},
threadPoolProperties = {
@HystrixProperty(name="coreSize",value="10"),//设置线程池大小
@HystrixProperty(name="queueSizeRejectionThreshold",value = "10")//设置队列最大等待请求数
}
)
@Override
public User queryUserById(Integer id) {
int random = new Random().nextInt(3000);
try {
System.out.println("sleep:"+random);
Thread.sleep(random);
} catch (InterruptedException e) {
}
return new User(id,"user"+id);
}
}
总结:做好熔断 避免你的服务引起蝴蝶效应