学科分类
目录
Spring Cloud

Hystrix的工作原理

为了大家更好地理解Hystrix的工作原理,下面我们借助Hystrix官网的工作流程图进行分析,具体如图1所示。

图1 Hystrix工作流程图

在图1所示的Hystrix工作流程中,整个流程大致可以分为以下8个步骤,具体介绍如下:

(1)实例化HystrixCommand或HystrixObservableCommand对象

之所以第一步实例化HystrixCommand或HystrixObservableCommand对象,是因为它们封装了对外部依赖访问的逻辑。如果请求的返回值只有一个,那么实例化HystrixCommand对象,示例代码如下:

 HystrixCommand command = new HystrixCommand(arg1, arg2); 

如果请求的返回值有多个,可以实例化HystrixObservableCommand对象,示例代码如下:

HystrixObservableCommand command = 
new HystrixObservableCommand (arg1, arg2);

需要注意的是,返回值有多个时,这些值都是被Observable对象所监听,所以返回的是一个Observable对象。

(2)调用Command方法触发操作指令

通过创建的HytrixCommand和HystrixObservableCommand实例调用相关方法执行操作指令。Hystrix API提供了4个触发流程的方法供开发者调用,具体如下:

● execute()方法:该方法是同步的,从依赖请求中接收到单个响应(或者出错时抛出异常)。

● queue()方法:调用外部依赖只返回一个值,返回一个Future对象。

● observe()方法:订阅一个从依赖请求中返回的Observable对象,这个Observable对象包含了从依赖服务返回的结果。

● toObservable()方法:返回一个Observable对象,只有当订阅一个从依赖请求中返回的Observable对象时,该方法才会执行Hystrix命令并返回结果。

以上4种调用方式,其实最后都是基于toObservable()方法来实现的。下面我们借助源代码来介绍。

HystrixCommand调用execute(),实际上就是调用queue().get()方法,具体代码如下:

public R execute() {
   return queue().get();
}

HystrixCommand调用queue(),实际调用的是toObservable().toBlocking().toFuture()方法,具体代码如下:

public Future<R> queue() {
     final Future<R> delegate = 
toObservable().toBlocking().toFuture();
      ....
 }

HystrixObservableCommand调用observe(),实际是调用的是toObservable().subscribe()方法,具体代码如下:

public Observable<R> observe() {
....
    final Subscription sourceSubscription = 
toObservable().subscribe(subject);
....
}

通过上面3个代码段,我们可以看出execute()方法内部实际上就是返回了对queue()方法的调用,而queue()方法和observe()方法内部返回的是调用toObservable()方法的结果。所以,归根结底,execute()、queue()和observe()都是基于toObservable()方法来实现的。

(3)根据依赖调用的结果缓存情况进行相应处理

执行操作指令时,Hystrix首先会检查缓存内是否有对应指令的结果,如果有的话,将缓存的结果直接以Observable对象的形式返回。

(4)判断熔断器是否开启

如果没有对应的缓存,Hystrix会检查Circuit Breaker的状态。如果Circuit Breaker的状态为开启状态,Hystrix将不会执行对应指令,而是而是直接路由到图5-10中第8步(Fallback),获取fallback方法,并执行fallback逻辑。

(5)判断线程池/队列/信号资源是否已满

如果Circuit Breaker的状态为关闭状态,Hystrix会继续进行线程池、任务队列、信号量的检查(图5-10中第5步),确认是否有足够的资源执行操作指令。如果资源满,Hystrix同样将不会执行对应指令,而是立即跳到图5-10中的第8步,执行fallback逻辑。

(6)执行HystrixObservableCommand.construct()或HystrixCommand.run()

如果资源充足,Hystrix将会执行操作指令。操作指令的调用最终都会到这两个方法:

● HystrixCommand.run(): 返回单个响应或者抛出异常。

● HystrixObservableCommand.construct():返回一个发射响应的 Observable或者发送一个onError()的通知。

如果执行run()方法或者construct()方法的执行时间大于命令所设置的超时时间值,那么该线程将会抛出一个TimeoutException异常(或者如果该命令没有运行在它自己的线程中,会提示[or a separate timer thread will, if the command itself is not running in its own thread])。在这种情况下,Hystrix将会路由到第8步,执行fallback逻辑,并且如果run()或者construct()方法没有被取消或者中断,会丢弃这上述两个方法最终返回的结果。

如果命令最终返回了响应并且没有抛出任何异常,Hystrix在返回响应后会返回一些日志信息,如果是调用run()方法,Hystrix会返回一个Observable,该Observable会发送单个响应并且会调用onCompleted()方法来通知响应的回调,如果是调用construct()方法,Hystrix会通过construct()方法返回相同的Observable对象。

(7)计算熔断器执行数据

Hystrix会报告成功、失败、拒绝和超时的指标给熔断器,熔断器包含了一系列的滑动窗口数据,并通过该数据进行统计。

Hystrix使用这些统计数据来决定熔断器是否应该熔断,如果需要熔断,将在一定的时间内不再请求依赖(这个时间可以通过配置指定),当再一次检查请求的健康的话会重新关闭熔断器。

(8)调用降级方法或者返回依赖请求的真正结果

当Hystrix命令执行失败时,Hystrix会尝试执行自定义的fallback逻辑,命令执行失败有以下几种情况:

● 当construct()或者run()方法执行过程中抛出异常。

● 当熔断器打开,命令的执行进入了熔断状态。

● 当命令执行的线程池和队列或者信号量已经满容。

● 命令执行超时。

如果Hystrix命令执行成功,它将以Observable形式返回响应给调用者。根据第(2)步的调用方式不同,在返回Observable之前可能会做一些转换,具体如图2所示。

图2 Hystrix指令API转换图

关于图5-11各部分的相关介绍具体如下:

● toObservable(): 返回原始的Observable,必须通过订阅它才会真正触发命令的执行流程。

● observe(): 在toObservable()产生原始Observable之后立即订阅它,让命令能够马上开始异步执行,并返回一个Observable对象,当调用它的subscribe时,将重新产生结果和通知给订阅者。

● queue(): 将toObservable()产生的原始Observable通过toBlocking()方法转换成BlockingObservable对象,并调用它的toFuture()方法返回异步的Future对象

● execute(): 在queue()产生异步结果Future对象之后,通过调用get()方法阻塞并等待结果的返回。

点击此处
隐藏目录