隔离是一种常见的风险控制(保护)手段,Hystrix 也使用了隔离策略,称之为 bulkhead pattern,翻译为:舱壁隔离模式(舱壁将船体内部空间划分成多个舱室,将舱与舱之间严密分开,在航行中,即使有舱破损进水,水也不会流到其他舱)
1. Hystrix 隔离
1.1 Thread Pool
将各依赖服务的访问交由独立的线程池来处理,会为每个依赖服务创建一个线程池。虽然可以起到很好的隔离作用,但也增加了计算开销,每个命令的执行都涉及到queueing、scheduling、context switching。
1.2 Semaphore
通过为各依赖服务设置信号量(或计数器)来限制并发调用,相当于对各依赖服务做限流。信号量模式下任务由当前线程直接处理,不涉及到线程切换,自然也就没有超时控制。
在Hystrix的工作机制中,在判断熔断之后,准备执行任务前,会先判断信号量拒绝和线程池拒绝的情况
分成线程池模式执行,信号量模式执行
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2. 请求缓存
微服务中,服务之间的依赖非常多,为了提升性能,如果每个方法都自行处理缓存的话,应用中可以想象有多少累赘的缓存代码。在请求生命周期内,无论是当前线程,还是其他线程,只要请求相同的数据,就自动做缓存,不侵入业务代码。
2.1 ReplaySubject
Hystrix 的请求缓存用的就是RxJava 中的 ReplaySubject 这个特性。replay 译为重放,Subject 是个合体工具,既可以做数据发射器(被观察者、Observable),也可以做数据消费者(观察者、Observer)。如果请求相同数据,就把原先的结果发你一份。
2.2 缓存的生命周期
缓存是请求周期request scope,在同一个请求范围内(Thread),可以使用相同cacheKey已缓存的结果
AbstractCommand.toObservable()中关于请求缓存的源码。请求缓存有2个条件:
- 启用了请求缓存
- 有相同cacheKey
整个逻辑还是非常简单的,在启用缓存的前提后,有缓存则读缓存,没缓存则缓存结果供下次使用
HystrixRequestCache实例的存储是由自身的静态变量搞定,未提供public的构造器,通过 getInstance() 的静态方法来获取与cacheKey对应的实例。
如何使用缓存的结果
由于toObservable()拿到的是一个ReplaySubject,下次命令再次执行时,订阅ReplaySubject后,可以直接拿到之前已有的结果。
Hystrix 执行结果缓存
1.通过一个HystrixCommand 来调用
2.判断请求结果缓存被弃用,并且在缓存中有数据,就直接返回缓存的结果
3.判断断路器是否打开,如果打开,就直接短路,执行fallback 回调函数
4.断路器没有打开,就根据信号量或者线程池 判断是否拒绝请求,拒绝请求就调用FallBack
5.断路器没有打开,不拒绝请求,就直接执行返回结果