通过Netflix Hystrix
官方公布的流程图,我们来了解一下Hystrix
的工作流程
1. 创建HystrixCommand
对象或者HystrixObservableCommand
对象
首先, 创建一个HystrixCommand
对象或者HystrixObservableCommand
对象用来表示对依赖服务的操作请求, 同时传递所有需要的参数, 它采用了命令模式来实现对服务调用操作的封装, 这两个Command
对象分别针对不同的应用场景.
-
HystrixCommand
: 依赖服务每次返回单一的回应。
HystrixCommand command = new HystrixCommand(arg1, arg2);
-
HystrixObservableCommand
: 若期望依赖服务返回一个Observable
, 并应用Observer
模式监听依赖服务的回应, 用在依赖的服务返回多个操作结果的时候.
HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);
1.1 命令模式
将请求封装成对象, 使用不同的请求,队列或者日志请求来参数化其他对象, 就是命令模式. 命令模式也可以支持撤销操作. 当需要将请求的对象和执行请求的对象解耦的时候, 可以考虑使用命令模式.
1.2 命令模式的要点
- 命令模式将发出请求的对象和执行请求的对象解耦;
- 在被解耦的两者之间是通过命令对象进行沟通的, 命令对象封装了接收者和一个或一组动作;
- 调用者通过调用命令对象的
execute()
方法发出请求, 这会使得接收者的动作被调用; - 调用者可以接受命令当作参数, 甚至在运行时动态地进行;
- 命令可以支持撤销, 做法是实现一个
undo()
方法来回到execute()
方法被执行前的状态;
2. 调用Command
的执行方法
执行Command
就可以发起一次对依赖服务的调用.
要执行Command
, 需要在4个方法中选择其中一个, 但主要是使用仅对HystrixCommand适用的两个:
-
execute()
: 同步执行, 从依赖的服务返回一个单一的结果对象, 或者是在发生错误的时候抛出异常. -
queue()
: 异步执行, 直接返回一个Future对象, 其中包含了服务执行结束时要返回的单一结果对象.
R value = command.execute();
Future<R> fValue = command.queue();
而另外两种执行方式, 适用于HystrixObservableCommand
:
-
observe()
: 订阅一个Observable
对象,Observable
代表的是依赖服务返回的结果, 获取到一个那个代表结果的Observable
对象的拷贝对象, 是一个 hot observable; -
toObservable()
: 返回一个Observable
对象, 如果我们订阅这个对象, 就会执行command
并且获取返回结果, 是一个 cold observable
Observable<R> ohValue = command.observe(); //hot observable
Observable<R> ocValue = command.toObservable(); //cold observable
execute()
实际上会调用queue().get().queue()
, 接着会调用toObservable().toBlocking().toFuture()
, 也就是说, 无论是哪种执行command
的方式, 最终都是依赖toObservable()
去执行的.
3. 检查是否开启缓存
若当前命令的请求缓存功能(request cache)是被启用的, 并且该命令缓存命中, 那么缓存的结果会立即以Observable
对象的形式返回.
4. 检查是否开启了短路器
在命令结果没有被缓存命中的时候, Hystrix
会在执行命令前检查断路器是否为打开状态:
- 如果断路器是打开的, 那么
Hystrix
不会执行命令, 而是转接到fallback
处理逻辑(第8步). - 如果断路器是关闭的,
Hystrix
会检查是否有可用资源来执行命令(第5步).
5. 检查线程池/队列/semaphore是否已经满了
如果command
对应的线程池, 列队, semeaphore
已经满了, 那么也不会执行command
, 而是直接调用fallback
降级机制.
6. 执行command
Hystrix
会根据我们编写的方法来决定采取什么样的方式去请求依赖服务.
HystrixCommand.run()
--返回单个响应或抛出异常.HystrixObservableCommand.construct()
--返回Observable
对象来发射多个结果, 或通过onError
发送错误通知.
如果HystrixCommand.run()
或HystrixObservableCommand.construct()
的执行, 超过了timeout
时长的话, 那么command
所在的线程就会抛出一个TimeoutException
.
如果timeout
了, 也会去执行fallback
降级机制, 而且就不会管run()
或construct()
返回的值了.
如果没有timeout
的话, 那么就会拿到一些调用依赖服务获取到的结果, 然后hystrix
会做一些logging
记录和metric
统计.
7. 短路健康检查
Hystrix
会将每一个依赖服务的调用成功, 失败, 拒绝, 超时等事件, 都会发送给circuit breaker
断路器.
短路器就会对调用成功/失败/拒绝/超时等事件的次数进行统计.
短路器会根据这些统计次数来决定, 是否要进行短路, 如果打开了短路器, 那么在一段时间内就会直接短路, 然后如果在之后第一次检查发现调用成功了, 就关闭断路器.
8. fallback处理
当命令执行失败时, Hystrix
会进入fallback
尝试回退处理, 也叫服务降级, 可以引入服务降级的请求有下面几种:
run()
或construct()
抛出一个异常- 短路器打开
- 线程池/队列/
semaphore
满了command
执行超时了
一般在降级机制中, 都建议给出一些默认的返回值, 比如静态的一些代码逻辑, 或者从内存中的缓存中提取一些数据, 尽量在这里不要再进行网络请求了.
即使在降级中, 一定要进行网络调用, 也应该将那个调用放在一个HystrixCommand
中,进行隔离.
- 在
HystrixCommand
中,上线getFallback()
方法, 可以提供降级机制. - 在
HystirxObservableCommand
中, 实现一个resumeWithFallback()
方法, 返回一个Observable
对象, 可以提供降级结果.
如果fallback
返回了结果,那么hystrix
就会返回这个结果.
- 对于
HystrixCommand
, 会返回一个Observable
对象, 其中会发返回对应的结果; - 对于
HystrixObservableCommand
, 会返回一个原始的Observable
对象.
如果没有实现fallback
,或者是fallback
抛出了异常, Hystrix
会返回一个Observable
, 但是不会返回任何数据.
不同的command
执行方式,其fallback
为空或者异常时的返回结果不同:
-
execute()
——抛出异常 -
queue()
——成功时返回java.util.concurrent.Future
, 但如果调用Future.get()
将抛出异常 -
observe()
——返回Observable
对象, 当你订阅该Observable
时, 将会立即终止并且调用订阅者的onError
方法. -
toObservable()
——同observe()
9. 返回成功的响应
当Hystrix
命令执行成功后, 会将处理结果直接返回或是以Observable
的形式返回, 具体怎么返回要根据执行命令的方式来区分, 下图是四种执行方式的依赖关系:
-
execute()
, 获取一个Future.get()
, 然后拿到单个结果; -
queue()
, 返回一个Future
; -
observer()
, 立即订阅Observable
, 然后启动8大执行步骤, 返回一个拷贝的Observable
, 订阅时理解回调给你结果; -
toObservable()
, 返回一个原始的Observable
, 必须手动订阅才会去执行8大步骤.
参考文章:
1 https://www.jianshu.com/p/cd0d7ac31596
2 https://www.cnblogs.com/holly8/p/11226177.html
3 https://my.oschina.net/u/4006148/blog/3176044