八、Hystrix 工作流程解析

Hystrix内部存在一套精细、高效和简洁的流程,该流程为实现hystrix的服务降级、故障隔离、访问限流和对被依赖服务运行信息的维护奠定了坚实的基础。

Hystrix整体工作流程
hystrix整个工作流程涉及到熔断器、缓冲、线程池等组件。这些组件分别实现了故障隔离、访问合并、服务降级和访问限流等功能,正是这些组件高效密切的配合确保了服务的稳定运行。

下面是hystrix的整体工作流程图:


整个流程可以大致归纳为如下几个步骤:
  1. 创建HystrixCommand或者HystrixObservableCommand对象
  2. 执行 Command
  3. 检查请求结果是否被缓存
  4. 检查是否开启了短路器
  5. 检查 线程池/队列/semaphore 是否已经满
  6. 执行 HystrixObservableCommand.construct() or HystrixCommand.run()
  7. 计算短路健康状况
  8. 调用fallback降级机制
  9. 返回依赖请求的真正结果

一、创建Command对象
HytrixCommand和HystrixObservableCommand包装了对外部依赖访问的逻辑。整个流程的第一个步骤就是实例化HystrixCommand或者HystrixObservableCommand对象。在构造这两个Command对象时,可以通过构造方法传递任何执行过程中需要的参数。

如果对外部依赖调用只返回一个结果值,那么可以实例化一个HystrixCommand对象
HystrixCommand command = newHystrixCommand ( arg1 , arg2 );

如果在调用外部依赖时需要返回多个结果值时,可以实例化一个HystrixObservableCommand对象
HystrixObservableCommand command = newHystrixObservableCommand ( arg1 , arg2 );


二、执行 Command
Hystrix API提供了四个方法来执行Command,分别如下:
  1. execute()方法,调用后直接block住,属于同步调用,直到依赖服务返回单条结果,或者抛出异常
  2. queue()方法,返回一个Future,属于异步调用,后面可以通过Future获取单条结果
  3. observe()方法,订阅(subscribe)一个Observable对象,Observable代表的是依赖服务返回的结果,获取到一个那个代表结果的Observable对象的拷贝对象
  4. toObservable()方法,返回一个Observable对象,如果我们订阅(subscribe)这个对象,就会执行command
其中execute()和queue()仅仅对HystrixCommand适用,适用于外部依赖只返回一个结果的情况下 。execute()方法调用的是HystrixCommand.queue().get()方法获取最终的返回值。而queue()方法调用的是HystrixCommand.toObservable().toBlocking().toFuture() 方法来实现的。也就是说,无论是哪种执行command的方式,最终都是依赖toObservable()去执行的。只不过是HystrixCommand这两个方法返回的结果只有一个值。


三、检查请求结果是否被缓存
如果开启了Hystrix请求结果缓存功能,并且这个调用的结果在缓存中存在,那么hystrix会直接将缓存的结果返回。在使用HystrixCommand和HystrixObservableCommand类封装依赖请求逻辑时,可以通过重载getCacheKey()实现请求结果的缓存。通过实现同类请求结果的缓存,可以在同一个请求Context中有效降低对外部依赖的实际调用次数。

以下是在两个线程中对外部依赖http请求的流程示意图:

结果缓存的好处:
1、在同一个请求上下文中,可以减少使用相同参数请求原始服务的开销。
2、请求缓存在 run() 和 construct() 执行之前生效,所以可以有效减少不必要的线程开销。


四、检查是否开启了短路器
接下来需要判断熔断器的开启状态。如果断路器被打开了,那么hystrix就不会执行这个command,而是直接去执行fallback降级机制。HystrixCircuitBreaker是Hystrix中实现熔断器功能的核心类,HystrixCommand和HystrixObservableCommand通过与HystrixCircuitBreaker交互实现了熔断器功能的开启和关闭。

下面是HystrixCircuitBreaker和Command的交互流程和内部开关控制逻辑:

熔断器开关控制条件:
  • 对外部依赖调用的次数满足配置的阈值
  • 对外部依赖调用发生错误的比率满足配置的阈值
在满足以上两个条件时,熔断器打开熔断开关,之后所有对外部依赖调用都将被直接断开。在开关打开时长超过试探窗口期后,熔断器将尝试放行部分外部依赖的调用,根据试探的结果决定重新开启或则关闭熔断开关。


五、检查 线程池/队列/semaphore 是否已经满
Hystrix引入了ThreadPool和semaphore两种方式实现资源隔离机制。如果command对应的线程池/队列/semaphore已经满了,那么也不会执行command,而是直接去调用fallback降级机制



六、执行 HystrixObservableCommand.construct() or HystrixCommand.run()
HystrixCommand和HystrixObservableCommand封装了外部依赖调用。通过重写HystrixCommand的run()方法和HystrixObservableCommand类的construct()方法实现外部依赖的调用。
  • HystrixCommand的run()方法只返回一个请求结果
  • HystrixObservableCommand的construct()方法返回一个Observable对象,通过订阅该Observerable可以获取多个返回结果

如果run()方法或则construct()方法在执行外部依赖请求出现超时,那么请求线程或者调用线程将抛出TimeOutException。Hystrix接下来直接返回调用降级方法获得的值而不管超时后外部依赖实际返回结果。除了通过抛出一个InterruptException外没有更好的方式中断外部调用线程的执行,所以建议调用外部依赖逻辑添加对InterruptException的处理以便在必要时中断请求线程的执行,而目前很多Http Client的实现并没有很好的支持该中断的响应,所以在使用Http Client发送http请求时需要合理设置超时时间以免长时间阻塞执行线程。

如果外部依赖调用正常返回,那么Hystrix metrics将进行必要的数据统计,这些统计数据关系到HystrixCircuitBreaker的正确运转。

七、计算短路健康状况
Hystrix会将每一个依赖服务的调用成功,失败,拒绝,超时,等事件,都会发送给circuit breaker断路器,HystrixCircuitBreaker通过维护一系列的counter记录外部依赖请求的执行情况。断路器根据维护的这些信息,在符合触发条件下开启断路功能,在条件合适的时候关闭断路开关。如果打开了断路器,那么在一段时间内就会直接短路,然后如果在之后第一次检查发现调用成功了,就关闭断路器。


八、调用fallback降级机制
在以下几种情况中,hystrix会调用fallback降级机制:run()或construct()抛出一个异常,短路器打开,线程池/队列/semaphore满了,command执行超时了。

一般在降级机制中,都建议给出一些默认的返回值,比如静态的一些代码逻辑,或者从内存中的缓存中提取一些数据,尽量在这里不要再进行网络请求了; 即使在降级中,一定要进行网络调用,也应该将那个调用放在一个HystrixCommand中,进行隔离

HystrixCommand通过重载getFallback()方法或则HystrixObservableCommand的resumeWithFallback()方法实现服务的降级。通过getFallback()方法只能返回一个结果值,而通过resumeWithFallback()方法返回的Observable对象可以给请求线程返回多个结果值。

如果fallback返回了结果,那么hystrix就会返回这个结果。对于HystrixCommand,会返回一个Observable对象,其中会发返回对应的结果; 对于HystrixObservableCommand,会返回一个原始的Observable对象。

如果没有实现fallback,或者是fallback抛出了异常,Hystrix会返回一个Observable,但是不会返回任何数据。

不同的command执行方式,其fallback为空或者异常时的返回结果不同
  • 对于execute(),直接抛出异常
  • 对于queue(),返回一个Future,调用get()时抛出异常
  • 对于observe(),返回一个Observable对象,但是调用subscribe()方法订阅它时,理解抛出调用者的onError方法
  • 对于toObservable(),返回一个Observable对象,但是调用subscribe()方法订阅它时,理解抛出调用者的onError方法


九、返回依赖请求的真正结果
外部依赖调用成功时将通过Observable对象返回结果值,鉴于构造Command对象的不同和调用执行方法的不同,结果值可能经过一些列的转化过程,不过从本质上讲都是通过Observable对象返回结果值。

外部依赖结果值返回流程图:


不同执行方式:
  • execute(),获取一个Future.get(),然后拿到单个结果
  • queue(),返回一个Future
  • observer(),立即订阅Observable,然后启动8大执行步骤,返回一个拷贝的Observable,订阅时理解回调给你结果
  • toObservable(),返回一个原始的Observable,必须手动订阅才会去执行8大步骤

猜你喜欢

转载自blog.csdn.net/sun_qiangwei/article/details/80376769
今日推荐