Hystrix是如何工作的
接上一篇:《Hystrix介绍》
流程图
下面这幅图相当重要
稍微解释一下上面的流程:
- Construct a HystrixCommand or HystrixObservableCommand Object
- Execute the Command
- Is the Response Cached?
- Is the Circuit Open?
- Is the Thread Pool/Queue/Semaphore Full?
- HystrixObservableCommand.construct() or HystrixCommand.run()
- Calculate Circuit Health
- Get the Fallback
- Return the Successful Response
1. 构造一个HystrixCommand或者HystrixObservableCommand对象
第一步是构造一个HystrixCommand或HystrixObservableCommand对象来表示对依赖项的请求。例如:
HystrixCommand command = new HystrixCommand(arg1, arg2); HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);
2. 执行命令
你可以用下列四种中的任意一种方式执行命令:
- execute() — 阻塞,直到收到响应或者抛出异常.
- queue() — 返回一个Future
- observe() — 订阅代表响应的Observable
- toObservable() — 返回一个Observable,当你订阅它以后,将会执行Hystrix命令并且推送它的响应
execute()方法是同步执行的,它是调用queue().get()
queue()方法是异步执行的,它调用的是toObservable().toBlocking().toFuture()
所以最终每个HystrixCommand都是一个Observable的实现
3. 响应是否已经被缓存?
如果请求在缓存中可用,并且该请求对应的响应在缓存中也是可用的,那么缓存的这个响应将立即以一个Observable的形式被返回
4. 断路器是否打开?
当你执行命令的时候,Hystrix检查断路器是否是开着的。如果是开着的,那么Hystrix不会执行命令,而是路由到第8步执行回退逻辑。如果是关着的,将执行第5步,继续检查容量是否可用。
5. 线程池/队列/信号量是否已经满了?
如果这个命令所关联的线程池和队列(或者信号量)是否满了,如果是,那么Hystrix不会执行命令,而是立即路由到第8不执行回退逻辑
6. HystrixObservableCommand.construct() or HystrixCommand.run()
我们知道对依赖的调用请求都是封装成HystrixCommand或者HystrixObservableCommand执行的,而真正执行的逻辑是写在HystrixObservableCommand.construct()或者HystrixCommand.run()中的:
- HystrixCommand.run() — 返回一个响应或者抛出一个异常
- HystrixObservableCommand.construct() — 返回一个Observable,并且推送响应或者发送一个onError通知
如果在执行run()或者construct()方法超时,那么线程将抛出一个TimeoutException。
7. 计算电路健康
Hystrix报告成功、失败、拒绝、超时给断路器,断路器维护一组计算统计的计数器。
断路器用这些统计数据来决定什么时候应该“跳闸”,此时后续所有的请求都会被短路,直到一个恢复周期耗尽以后在第一次检查某些健康检查之后才会打开电路。
(PS:想象一下生活中的漏电保护器,电压过高时会自动跳闸,跳闸以后就没电了,家用电器都用不了了,之后你可以自己不去合上,当然前提是你不能再用那些大功率电器了,于是来电了)
8. 回退
当命令执行失败时,Hystrix试图恢复到您的fallback:当run()或者construct()抛出异常;当由于断路器跳闸导致命令被短路;当命令的线程池或队列容量满了;或者当命令执行超时。
在HystrixCommand的情况下,为了提供回退逻辑,你可以实现HystrixCommand.getfallback(),它返回一个单一的回退值。
对于HystrixObservableCommand(),为了提供回退逻辑,你需要实现hystrixobservablecomman.resumewithfallback(),它会返回一个Observable,这个Observable可能返回一个回退值或者多个值。
如果你没有实现一个fallback方法,或者fallback它自己抛了一个异常,此时Hystrix仍然会返回一个Observable,但是这个Observable什么都不会推送并立即发送一个onError通知。通过这个onError通知,造成命令失败的异常会传回给调用者。
9. 返回成功响应
如果Hystrix命令成功,它会返回响应给调用者,或者返回一个Observable。这取决于在第2步的时候你是如果调用命令的。
Circuit Breaker(断路器)
下图显示了HystrixCommand或HystrixObservableCommand是如何与HystrixCircuitBreaker交互的,以及它的逻辑和决策流程,包括断路器中计数器的行为。
上面的流程可以这样描述:
- 假设整个电路的体积达到一定的阈值
- 假设错误百分比超过设定的错误百分比阈值
- 断路器从CLOSED变成OPEN
- 当它打开时,经过这个断路器的所有请求被短路
- 过了一段时间以后,下一个单个请求被允许通过(此时是半打开状态)。如果这个请求失败,在接下来的睡眠窗口期间断路器保持OPEN状态;如果这个请求成功,断路器转成CLOSED状态;然后继续执行第1步,如此反复。
隔离
Hystrix使用隔板模式来隔离彼此的依赖关系,并限制对其中任何一个的并发访问。
线程和线程池
Hystrix使用独立的、每个依赖项的线程池作为约束任何给定依赖项的一种方式,因此底层执行的延迟只会使该池中的可用线程饱和。
线程池的好处
- 应用程序被完全保护而不受失控的客户端的影响。给定的依赖项的线程池可以填满,而不会影响应用程序的其余部分。
- 应用程序可以接收新的客户端请求
- 当失败的客户端再次恢复正常时,线程池将被清理,应用程序将立即恢复正常的性能
- 如果一个客户端配置错误,线程池的健康将会很快地证明这一点(通过增加错误、延迟、超时、拒绝等),并且您可以处理它(通常是通过动态属性进行实时处理),而不会影响应用程序的功能。
Semaphores(信号量)
你可以用信号量(或者计数器)来限制对任意依赖项并发调用的数量,代替用线程池的大小。
HystrixCommand 和 HystrixObservableCommand 在两个地方支持用信号量:
- Fallback:当Hystrix恢复fallbacks时它总是调用Tomcat的线程这样做
- Execution:如果设置属性execution.isolation.strategy为SEMAPHORE,那么,Hystrix将使用信号量而不是线程来限制调用该命令的发父线程的并数量。
https://github.com/Netflix/Hystrix/wiki/How-it-Works