背景
在微服务架构中,存在着很多的服务单元,若一个单元出现故障,就很容易因为依赖关系而导致故障的蔓延,最终导致整个系统的瘫痪,为了解决这个问题,产生了断路器等一系列服务保护机制。SpringCloud Hystrx具备服务降级,服务熔断,请求缓存,请求合并以及服务监控等强大功能
雪崩效应的常见场景
基础服务的故障会导致其他服务发生故障,服务与服务之间相互依赖,从而发生崩溃,我们称这种现象为雪崩效应。它的常见场景如下:
- 硬件故障:服务器宕机,机房断电
- 流量激增
- 缓存穿透
- 程序BUG
- 同步等待:服务间采用同步调用方式,同步等待导致资源耗尽
缓存世界的三大问题及解决方案
初探Hystrix
首先要了解什么是命令模式:
举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。
命令模式包含三个角色:
- Invoker:命令的请求者
- Receiver:命令的执行者
- Command:定义命令的统一接口
命令模式是为了降低命令的请求者和命令的实现者之间的耦合关系。
Hystrix的工作流程
- 构造一个HystrixCommand或HystrixObservableCommand对象,用于封装请求
- 执行命令,Hystrix提供了4种执行命令的方法
- 判断是否使用缓存响应请求,若启动了缓存且缓存可用,直接使用缓存响应请求
- 判断熔断器是否打开,如果打开跳到第8步
- 判断线程池/信号量是否已满,已满则跳到第8步
- 执行HystrixCommand.run()或HystrixObservableCommand.construct(),如果执行失败或超时,跳到第8步,否则跳到第9步
- 统计熔断监控指标
- 走Fallback备用逻辑
- 返回响应请求
Hystrix执行命令的几种方法
execute()和queue()适用于HystrixCommand对象,而observe()和toObserveable()方法适用于HystrixObservableComand对象
- execute():以同步阻塞方式实现run(),只支持接收一个值对象。
- queue():以异步非阻塞方式执行run(),只支持接收一个值对象。
- observe():如果继承的是HystrixCommand,Hystrix会以非阻塞方式执行run();如果继承的是HystrixObservableCommand,将以阻塞方式执行construct()。支持接收多个值对象。
- toObservable():基本过程与observe()类似,不同的地方在于调用observe()返回的是hot Observable,也就是说observe()会自行触发run()/construct(),无论是否存在订阅者;而调用toObservable()方法返回的是cold Observable,不会立即触发run()/construct(),必须有订阅者订阅Observable才行
Hystrix容错
Hystrix的容错主要是通过添加容许延迟和容错方法,帮助控制这些分布式服务之间的交互。还通过隔离服务之间的访问点,阻止他们之间的联级故障以及提供回退选项来提高系统的弹性。Hystrix主要有以下几种容错方法:
- 资源隔离
- 熔断
- 降级
资源隔离
在货船中,为了防止漏水和火灾的扩散,一般会将货仓进行分割,避免了一个货仓出事导致整艘船沉没的悲剧。同样的,在Hystrix中,也采用了这样的舱壁模式,将系统中的服务提供者隔离起来,一个服务提供者延迟升高或者失败,并不会导致整个系统的失败
Hystrix提供了两种资源隔离的方式:线程池和信号量
线程隔离-线程池
Hystrix通过命令模式对发送请求的对象和执行请求的对象进行解耦。当第一次创建Command时,根据配置创建一个线程池,后续相同的请求创建Command时,将会重用已创建的线程池 。通过将发送请求线程与执行请求的线程分离,可有效防止发生级联故障。当线程池或请求队列饱和时,Hystrix将拒绝服务,走Fallback备用逻辑
线程池隔离的优缺点
优点:
- 保护应用程序以免受到来自依赖故障的影响
缺点:
- 每个命令的执行都在单独的线程中完成,增加了排队,调度和上下文切换的开销
线程隔离-信号量
客户端向依赖服务发起请求时,首先要获取一个信号量才能真正发起调用,由于信号量的数量有限,当并发请求量超过信号量个数时,后续的操作都会直接拒绝。信号量隔离主要是通过控制并发请求量,达到限流的目的
熔断+限流
Hystrix向熔断器报告成功,失败,超时和拒绝的状态,熔断器维护并统计这些数据,并根据这些统计信息来决策熔断开关是否打开。如果打开,熔断后续请求,走FallBack备用逻辑。每隔一段时间后(默认5秒)熔断器尝试半开,放入一部分请求流量进来,如果请求成功,熔断器关闭
熔断器工作原理
- 判断是否允许将请求提交到线程池
- 判断熔断器是否打开。如果熔断器打开进入第三步;否则继续判断如果一个周期内总的请求数小于requestVolumeThreshold的值,请求允许放行;否则继续判断如果一个周期内错误率小于errorThresholdPercentage的值,请求允许放行,否则打开熔断器进入第三步
3.如果熔断器打开,且距离熔断器打开时间或上一次试探请求时间超过sleepWindowInMilliseconds值,熔断器进入半开状态允许放行一个试探请求,否则不允许放行
回退降级
降级,通常情况下是指业务高峰期,为了保证核心服务正常运行,需要停掉一些不太重要的业务;或者某些服务不可用时,执行备用逻辑以保证主体业务不受影响
降级回退方式
- Fail Fast快速失败:发生故障直接抛出异常
- Fail Silent无声失败:在降级方法中返回null,空map,空list等响应
- Fallback:Static:在降级方法中返回静态默认值
- Fallback:Cache via Network:如果调用依赖服务失败,可以从缓存服务在查询旧数据版本
- 主次模型:如当系统升级新版本时,如果新版本的功能出现问题,通过开关控制降级调用旧版本的功能