承接上篇内容本节具体聊聊Hystrix详细的工作原理以及内部实现流程。
Hystrix向服务依赖项发出请求时会发生什么?
1. 构造一个HystrixCommand或HystrixObservableCommand对象
第一步是构造一个HystrixCommand或HystrixObservableCommand对象来表示对依赖项的请求。将请求发出时需要的任何参数传递给构造函数。
构造一个 HystrixCommand
对象 如果依赖期望返回一个单个响应. 例如:
HystrixCommand command = new HystrixCommand(HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool);
构造一个 HystrixObservableCommand
对象 如果期望依赖项返回发出响应的Observable对象.例如:
HystrixObservableCommand ObservableCommand = new HystrixObservableCommand(HystrixCommandGroupKey group)
2. 执行命令
有四种方法可以执行命令,使用以下四种方法之一的Hystrix命令对象(前两种方法只适用于简单的HystrixCommand对象,不适用于HystrixObservableCommand):
-
execute()
— blocks, 返回从依赖项接收到的单个响应(或在出现错误时抛出异常) -
queue()
— 返回一个Future值,使用该将来值可以从依赖项获得单个响应 -
observe()
— 订阅表示来自依赖项的响应的Observable 对象,并返回复制源observable对象的observable对象。 -
toObservable()
— 返回一个可观察对象,当您订阅它时,它将执行Hystrix命令并发出响应。
K value = command.execute();
Future<K> fValue = command.queue();
Observable<K> ohValue = command.observe(); //hot observable
Observable<K> ocValue = command.toObservable(); //cold observable
同步调用execute()调用queue().get()。
queue()依次调用toObservable(). toblocking (). tofuture()。也就是说,最终每个HystrixCommand都由一个可观察到的实现支持,即使是那些打算返回单个简单值的命令。
3.是否缓存了响应?
如果为该命令启用了请求缓存,并且在缓存中可用对请求的响应,则此缓存的响应将立即以可观察到的形式返回。(请参阅下面的Request Caching
)。
4. Circuit 是否打开?
当您执行该命令时,Hystrix将与断路器一起检查电路是否打开。
如果电路打开(或“跳闸”),那么Hystrix将不执行命令,而是将流路由到(8)获取回退。
如果电路被关闭,则流继续到(5),检查是否有可用的容量来运行命令。
5.Thread Pool/Queue/Semaphore 是否已满?
如果与该命令关联的线程池和队列(或信号量,如果不在线程中运行)已满,那么Hystrix将不执行该命令,而是立即将流路由到(8)获取回退。
6. HystrixObservableCommand.construct() or HystrixCommand.run()
这里,Hystrix通过为此目的编写的方法调用对依赖项的请求,方法如下:
-
HystrixCommand.run()
— 返回单一响应或抛出一个异常。 -
HystrixObservableCommand.construct()
— 返回发出响应或发送‘onError’通知的可观察对象。
如果run()或construct()方法超过命令的超时值,线程将抛出一个TimeoutException(如果命令本身不在自己的线程中运行,则单独的计时器线程将抛出一个TimeoutException)。在这种情况下,Hystrix将响应路由到8。获取回退,如果最终返回值run()或construct()方法没有取消/中断,那么它将丢弃该方法。
请注意,没有办法强制潜在线程停止工作——Hystrix在JVM上能做的最好的事情就是抛出InterruptedException。如果由Hystrix包装的工作不尊重interruptedexception,那么Hystrix线程池中的线程将继续它的工作,尽管客户机已经收到了TimeoutException。这种行为可能会使Hystrix线程池饱和,尽管负载“正确释放”。大多数Java HTTP客户端库不解释interruptedexception。因此,请确保正确配置HTTP客户机上的连接和读/写超时。
7. 计算电路健康
Hystrix向断路器报告成功、失败、拒绝和超时,断路器维护一组滚动计数器,用于计算统计数据。它使用这些统计数据来确定电路应该在什么时候断路,在这一点上,它会短路任何后续的请求,直到恢复期结束,在此期间,它会在第一次检查某些健康检查之后再次关闭电路。
8. Get the Fallback
Hystrix试图恢复你的回滚命令执行失败时:当一个异常的构造()或()运行(6),当命令电路短路,因为打开(4),当命令的线程池和队列或信号能力(5),或者当命令已超过其超时长度。
编写回退以提供来自内存缓存或其他静态逻辑的通用响应,而不需要任何网络依赖。如果必须在回退中使用网络调用,则应该使用另一个HystrixCommand或HystrixObservableCommand。
对于HystrixCommand,要提供回退逻辑,可以实现HystrixCommand. getfallback(),它返回一个回退值。
对于HystrixObservableCommand,要提供回退逻辑,可以实现HystrixObservableCommand. resumewithfallback(),它返回一个可观察的对象,该对象可能会发出一个或多个回退值。
如果回退方法返回一个响应,那么Hystrix将把这个响应返回给调用者。在HystrixCommand.getFallback()的情况下,它将返回一个可观察的对象,该对象发出方法返回的值。在hystrixobservablecomman . resumewithfallback()的情况下,它将返回方法返回的相同的可观察值。
如果您还没有为您的Hystrix命令实现回退方法,或者回退本身引发异常,Hystrix仍然返回一个可观察到的方法,但是该方法不发出任何东西,并立即以onError通知终止。正是通过这个onError通知,导致命令失败的异常才被传输回调用者。(实现可能失败的回退实现是一种糟糕的实践。您应该实现回退,这样它就不会执行任何可能失败的逻辑。
失败或不存在回退的结果将根据您如何调用Hystrix命令而有所不同:
- execute() — throws an exception
- queue() — 成功返回一个Future,但是如果调用它的get()方法,这个Future将抛出异常
- observe() — 返回一个可观察到的对象,当您订阅该对象时,将通过调用订阅方的onError方法立即终止该对象
- toObservable() — 返回一个可观察对象,当您订阅该对象时,它将通过调用订阅方的onError方法终止
9. 返回成功的响应
如果Hystrix命令成功,它将以可观察到的形式返回响应或响应给调用者。根据您如何调用上面步骤2中的命令,这个可观察对象可能在返回给您之前进行转换:
- execute() — 以与.queue()相同的方式获取一个Future,然后在这个 Future上调用get()来获取可观察对象发出的单个值
- queue() — 将可观察对象转换为BlockingObservable,以便将其转换为未来,然后返回此未来
- observe() — 立即订阅可观察对象,并开始执行命令的流;返回一个可观察对象,当您订阅该对象时,将重播排放和通知
- toObservable() — 返回可观察值不变;您必须订阅它,才能真正开始执行命令的流程