1. 雪崩问题
假设有一个微服务 A,它调用了服务 B、服务 D,而某时刻服务 D 挂掉;
服务 A 要等待服务 D 的结果,而服务 D 已经不能正常响应了,此时服务 A 内部阻塞,不会释放 tomcat 的连接(此时服务 A 依赖于服务 B 的业务还是不受影响)。但随着这种服务 A 向服务 D 发送的请求越来越多,而 tomcat 的连接数有限,tomcat 资源就会耗尽;
此时,再有请求进到服务 A,哪怕是不需要访问服务 D 的请求,服务 A 也没资源处理了。即某个服务故障,最终导致了依赖它的某个服务也故障了。我们把这一点放大;
如果后面的服务与资源耗尽不可用的服务有依赖关系,则后面的服务也会渐渐变得不可用,从而引起整个链路中的所有微服务都不可用;
微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。
2、雪崩问题的四种解决方案
(1)超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止等待。
这只能缓解雪崩问题,而不能彻底根除,因为比如设置等待 1s 就释放 1 个连接,但一秒钟之内进来了 2 个请求,终有一天,服务 A 的资源也可能被耗尽。
(2)线程隔离(舱壁模式):限定每个业务能使用的线程数,避免耗尽整个 tomcat 的资源,因此也叫线程隔离。
舱壁模式来源于船舱的设计:
如上图,船舱都会被隔板分离为多个独立空间,当船体破损时,只会导致部分空间进入,将故障控制在一定范围内,避免整个船体都被淹没。
对应到程序中,我们可以限定每个业务能使用的线程数(将可用线程数分割),避免耗尽整个 tomcat 的资源,因此也叫线程隔离。如:服务 A 调用服务 B,最多占用 10 个线程,服务 A 调用服务 C 最多 10 个线程;
现在哪怕服务 A 到 C 出问题了,能占用的 tomcat 资源也是有限的,不至于所有资源被耗尽(不至于整条船全进水了),如此就解决了雪崩问题。但造成了一点资源浪费,明明知道服务 A 到 C 不通了,但还是在消耗资源去请求。
(3)熔断降级:由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。
比如服务 A 向服务 D 发送请求,访问了三次,两次失败,即 66% 异常,阈值如果设为 50%,则此时发生熔断,后续再有这个请求过来直接拦截,快速失败,立刻释放资源,从而解决雪崩问题。
下图为断路器的三个普通状态:关闭(CLOSED)、开启(OPEN)、半开(HALF_OPEN)。另外,还有两个特殊的状态(实际工作当中很少用到):禁用(DISABLED)、强制开启(FORCED_OPEN)。
—— Excalidraw - https://excalidraw.com/
(4)流量控制:限制业务访问的 QPS,避免服务因流量的突增而故障。
以上三种是出现雪崩后的解决方案,流量控制则是预防雪崩,防止服务发生故障。服务不故障,也就不传递给依赖它的服务,也就没有雪崩问题了。当然了,流量控制只是预防了因高并发引起的服务故障。服务故障的原因可不止高并发一种。
3、总结
(1)雪崩问题:微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况。
(2)限流:是对服务的保护,避免因瞬间高并发流量而导致服务故障,进而避免雪崩。是一种预防措施。
(3)超时处理、线程隔离、降级熔断:是在部分服务故障时,将故障控制在一定范围,避免雪崩。是一种补救措施。