1.说到隔离、熔断、降级,最出名的就是 Netflix 开源的 Hystrix 组件,Hystix官方对它描述为:Hystrix是一个延迟和容错库,旨在隔离远程系统、服务和第三方库,阻止级联故障,在复杂系统中实现恢复能力。
2.下图应用从单块到微服务,系统被拆分为多个,那么产生了个问题,就是微服务的可用性,假设单块应用的可用性是 99.99%,如果被拆分为30个微服务后,总体的可用性是多少,答案是 99.7%,每月2小时宕机时间,实际情况可能更长,30个微服务可用性算法是 99.99%的30阶乘(30个99.99%相乘)。
3.凡是依赖都可能会失败
当用户请求 A、P、H、I 四个服务获取数据时,在正常流量下系统稳定运行,如果某天系统进来大量流量,其中服务 I 出现 CPU、内存占用过高等问题,结果导致服务 I 出现延迟、响应过慢,随着请求的持续增加,服务 I 承受不住压力导致内部错误或资源耗尽,一直不响应,此时更糟糕的是其他服务对 I 有依赖,那么这些依赖 I 的服务一直等待 I 的响应,也会出现请求堆积、资源占用,慢慢扩散到所有微服务,引发雪崩效应。
4.为什么服务要进行隔离、熔断、降级?当服务出现异常时可以和其他服务隔离开来,不影响其他服务
- 隔离:将请求封装在HystrixCommand中,然后这些请求在一个独立的线程中执行,每个依赖服务维护一个小的线程池(或信号量),在调用失败或超时的情况下可以断开依赖调用或者返回指定逻辑
- 熔断:当HystrixCommand请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务,断路器保持在开路状态一段时间后(默认5秒),自动切换到半开路状态(HALF-OPEN),这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED),否则重新切换到开路状态(OPEN)
- 降级:服务降级是指当请求后端服务出现异常的时候, 可以使用fallback方法返回的值
5.基本的容错模式
- 1.主动超时:Http请求主动设置一个超时时间,超时就直接返回,不会造成服务堆积
- 2.限流:限制最大并发数
- 3.熔断:当错误数超过阈值时快速失败,不调用后端服务,同时隔一定时间放几个请求去重试后端服务是否能正常调用,如果成功则关闭熔断状态,失败则继续快速失败,直接返回。(此处有个重试,重试就是弹性恢复的能力)
- 4.隔离:把每个依赖或调用的服务都隔离开来,防止级联失败引起整体服务不可用
- 5.降级:服务失败或异常后,返回指定的默认信息
5.Hystrix 容错的流程图
上面有 9 个步骤,下面分别讲解每一步骤:
- 1.每个请求都会封装到 HystrixCommand 中
- 2.请求会以同步或异步的方式进行调用
- 3.判断熔断器是否打开,如果打开,它会直接跳转到 8 ,进行降级
- 4.判断线程池/队列/信号量是否跑满,如果跑满进入降级步骤8
- 5.如果前面没有错误,就调用 run 方法,运行依赖逻辑
- 5.运行方法可能会超时,超时后从 5a 到 8,进行降级
- 6.运行过程中如果发生异常,会从 6b 到 8,进行降级
- 6.运行正常会进入 6a,正常返回回去,同时把错误或正常调用结果告诉 7 (Calculate Circuit Health)
- 7.Calculate Circuit Health它是 Hystrix 的大脑,是否进行熔断是它通过错误和成功调用次数计算出来的
- 8.降级方法(8a没有实现降级、8b实现降级且成功运行、8c实现降级方法,但是出现异常)
- 8a.没有实现降级方法,直接返回异常信息回去
- 8b.实现降级方法,且降级方法运行成功,则返回降级后的默认信息回去
- 8c.实现降级方法,但是降级也可能出现异常,则返回异常信息回去
6.容错理念
- 凡是依赖都可能会失败
- 凡是资源都有限制(CPU、内存、线程池)
- 网络并不靠谱
- 延迟是应用稳定性最大杀手(可能由于延迟最终拖垮整个微服务,给服务设置一个超时时间来解决延迟引起的问题)
7.Hystrix Dashboard 仪表盘监控所有服务熔断情况
- 图中左边区域,Hosts表示有234个实例,Circuit Open(76)表示有76个实例打开了熔断开关,进入熔断状态,有158个实例没有进入熔断状态,右边的同理,但是右边所有的实例都处理熔断状态,所有请求都被拒绝,这种情况比较严重
下面是 Hystrix 的代码实践,以上的相关概念,可以更好的帮助我们使用 Hystrix ,下面会创建一个简单的项目,引入spring-cloud-starter-netflix-hystrix组件,在控制器的方法中手动写一个异常,同时给该方法添加一个 HystrixCommand降级方法,指定降级信息,访问该方法时,触发异常,返回降级信息
7.创建SpringBoot项目,引入Hystrix依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在启动主类上添加如下代码:
@RestController
@EnableHystrix
@SpringBootApplication
public class HystrixDemoApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDemoApplication.class, args);
}
//通过HystrixCommand注解,手动指定一个降级方法,出现异常后会调用该降级方法
@RequestMapping("/getName")
@HystrixCommand(fallbackMethod = "getNameFallback")
public String getName(String username) throws Exception{
if(username.equals("zhuyu")){
return "this is zhuyu";
}else{
throw new Exception();
}
}
/**
* 出错后会调用该降级方法,返回指定的信息
* @param username
* @return
*/
public String getNameFallback(String username){
return " this username is not exist ";
}
}
运行程序,访问 http://localhost:8080/getNameusername=zhuyu ,正常,没有触发降级
再访问 http://localhost:8080/getNameusername=zhuyu11 ,可以看到返回了降级后设置的信息
好了,最简单的降级代码完成了。
上面仅演示了HystrixCommand的降级配置,还有很多其他配置,如:指定HystrixCommand的超时时间、线程池key以及线程池和队列大小,大家可以自行实验
@HystrixCommand(fallbackMethod = "getNameFallback", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "4000")
},
threadPoolKey = "hystrixDemoThreadPool",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "maxQueueSize", value = "10")
})
Hystrix不仅仅有熔断、降级,实际生产环境中 Hystrix 需要与网关和Feign集成,对服务间的调用做熔断、降级
还有Hystrix Dashborad仪表盘,用可视化的面板展示系统在一段时间内发生的请求情况
Hystrix还可以把多个请求合并进行一次性处理,可以减少网络通信与线程池资源,对处理高并发的业务效果较好
网上有位朋友对 Hystrix的熔断、降级、隔离说的也很好:https://www.cnblogs.com/Leo_wl/p/9054906.html
代码已上传至码云,源码,项目使用的版本信息如下:
- SpringBoot 2.0.6.RELEASE
- SpringCloud Finchley.SR2