Hystrix介绍
Hystrix是什么
在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
Hystrix为了什么
Hystrix被设计的目标是:
- 对通过第三方客户端访问的依赖项的延迟和故障进行保护和控制。
- 在复杂的分布式系统中组织级联故障。
- 快速失败,快速恢复。
- 回退,尽可能优雅地降级
- 启用近实时监控、警报和操作控制。
Hystrix解决了什么问题
复杂分布式体系结构中的应用程序有许多依赖项,每个依赖项在某些时候都不可避免地会失败。如果主机应用程序没有与这些外部故障隔离,那么它有可能被他们拖垮。
例如,对于一个依赖于30个服务的应用程序,每个服务都有99.99%的正常运行时间,期望如下:
现实通常是更糟糕的!
当一切正常时,请求看起来是这样的
当其中有一个系统有延迟时,它可能阻塞整个用户请求:
在高流量的情况下,一个后端依赖项的延迟可能导致所有服务器上的所有资源在数秒内饱和(PS:意味着后续再有请求将无法立即提供服务)
Hystrix设计原则是什么
-
防止任何单个依赖项耗尽所有容器(如Tomcat)用户线程。
-
甩掉包袱,快速失败而不是排队。
-
在任何可行的地方提供回退,以保护用户不受失败的影响。
-
使用隔离技术(如隔离板、泳道和断路器模式)来限制任何一个依赖项的影响。
-
通过近实时的度量、监视和警报来优化发现时间。
-
通过配置的低延迟传播来优化恢复时间。
-
支持对Hystrix的大多数方面的动态属性更改,允许使用低延迟反馈循环进行实时操作修改。
-
避免在整个依赖客户端执行中出现故障,而不仅仅是在网络流量中。
Hystrix是如何实现它的目标的
- 用一个HystrixCommand 或者 HystrixObservableCommand (这是命令模式的一个例子)包装所有的对外部系统(或者依赖)的调用,典型地它们在一个单独的线程中执行
- 调用超时时间比你自己定义的阈值要长。有一个默认值,对于大多数的依赖项你是可以自定义超时时间的。
- 为每个依赖项维护一个小的线程池(或信号量);如果线程池满了,那么该依赖性将会立即拒绝请求,而不是排队。
- 调用的结果有这么几种:成功、失败(客户端抛出异常)、超时、拒绝。
- 在一段时间内,如果服务的错误百分比超过了一个阈值,就会触发一个断路器来停止对特定服务的所有请求,无论是手动的还是自动的。
- 当请求失败、被拒绝、超时或短路时,执行回退逻辑。
- 近实时监控指标和配置变化。
当你使用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步的时候你是如果调用命令的。
来源:https://www.cnblogs.com/cjsblog/p/9395584.html