Netflix Hystrix 是如何运行的

项目地址:Netflix Hystrix Github
文章译自:Hystrix/wiki/How-it-Works
阅读本文章前,请先了解 Hystrix 的一些基本概念( What is Hystrix )。

流程图

下图展示了当使用 Hystrix 构造请求,请求到一个服务依赖项时所发生的流程:
hystrix 运行流程图
下面部分将更详细地解释上述流程:
  1. 构造一个 HystrixCommand 或 HystrixObservableCommand 对象
  2. 执行 Command
  3. 是否缓存 Response
  4. 断路器是否打开
  5. 线程池 / 队列 / 信号量 是否已
  6. 执行 HystrixObservableCommand.construct() 或 HystrixCommand.run()
  7. 计算线路健康状况
  8. 获取回调
  9. 返回成功的 Response

1. 构造一个 HystrixCommand 或 HystrixObservableCommand 对象

第一步就是构造一个 HystrixCommand 或 HystrixObservableCommand 对象,**Command 对象代表对依赖项的请求。传递构造函数在请求发生时所需要的各个参数。

当依赖项预计会返回一个Response时,构造一个 HystrixCommand 对象,如:

HystrixCommand command = new HystrixCommand(arg1, arg2);

当依赖项预计会返回一个可传播 Response 的 Observable 时(rxJava),构造一个 HystrixObservableCommand 对象,如:

HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);

2. 执行 Command

执行命令有如下四种方式,可以选择一种方法去执行 Hystrix 的 Command 对象(第一,二个方法只适合简单的命令对象 HystrixCommand 执行,不适合 HystrixObservableCommand 对象):

  • execute()——阻塞,返回从依赖项接收到的响应(或有异常时抛出异常)
  • queue()——返回一个 java.util.concurrent.Future,可以通过 Future.get()来获取一个依赖项返回的响应
  • observe()——订阅 Observable(这个 Observable 代表从依赖项返回的响应),并返回复制该源 Observable 的 Observable
  • toObservable()——返回一个 Observable,当这个 Observable 被订阅时,将会执行对应的 Hystrix 的 Command 对象,并且传播返回的响应
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 的调用最终都是通过 Observable 来实现(netflix 的 oss 项目中大量使用了 rxJava),即使这些命令只是想返回单个值或者一些简单的值。

3. 是否缓存 Response

如果一个 Hystrix Command 的请求缓存被启用了,并且这个请求的响应在缓存中可用,那么这个被缓存的响应将会立即以 Observable 的形式返回(更详细看下面的“请求缓存”部分)。

4. 断路器是否开启

当执行一个命令,Hystrix 会检查断路器(circuit-breaker)确认断路器是否是开启状态。

如果断路器是开启状态(或者“跳闸”),那么 Hystrix 将不会执行这些命令,这些失败的命令将会被路由到回调逻辑进行后续处理。

如果断路器是关闭状态,那么执行流程将往下走到第5步:检查是否有足够的容量来运行该命令

扫描二维码关注公众号,回复: 475020 查看本文章

5. 线程池 / 队列 / 信号量 是否已满

Hystrix 的执行隔离策略有两种:Thread 和 Semaphore

如果命令相关联的线程池和队列(不是线程模式时,判断依据为信号量)已满,那么 Hystrix 将不会执行命令,这些失败的命令也会被路由到回调逻辑进行后续处理。

6. 执行 Command 中的业务逻辑

到这里,Hystrix 调用对依赖项的这些请求,方式如下:

  • HystrixCommand.run()——返回单个响应或抛出异常
  • HystrixObservableCommand.construct()——返回 Observable 传播收到的响应或者发送一个 onError的通知
    如果run()construct()方法执行时长超过了命令的超时阀值,其线程将抛出一个TimeoutException(或者在一个单独的线程抛出,如果命令没有运行在它自己的线程)。这种情况下 Hystrix 将路由响应到第8步,获取回调逻辑;并且如果该方法没有取消或中断,它将放弃run()construct()方法最终的返回值。

请注意,没有办法强制地停止延迟线程——Hystrix 能做的就是在 JVM 上抛出一个 InterruptedException。如果由 Hystrix 包装过的逻辑没有注意处理 InterruptedExceptions,则 Hystrix 线程池中的线程将会继续运行,即使客户端已收到TimeoutException。这种行为会填满 Hystrix 对应命令的线程池,尽管负载是“correctly shed”。很多 Java Http Client 库没有说明InterruptedExceptions,因此请确保在HTTP客户端上正确配置连接和读/写的超时时长。

如果命令没有抛出异常并且返回了响应,Hystrix 将会在执行一些日志记录和度量报告之后返回响应给调用者。如果是通过run()运行,Hystrix 将返回 Observable 传播单个响应,然后发送一个onCompleted的通知;如果是通过construct()运行,Hystrix 返回与construct()返回的相同的Observable。

7. 计算线路健康状况

Hystrix 向断路器报告成功,失败,拒绝和超时的数据,断路器维持一组实时跳动的计数器来统计数据。(HystrixCommandMetrics)

断路器使用这些统计数据来确定电路何时应该“跳闸”,什么时候该将所有后续的请求短路,直到恢复期过去,并在首次检查某些健康检查后会再次关闭电路。

8. 获取回调

当命令执行失败时,Hystrix 会试图恢复到 fallback 状态:当执行construct()run()时抛出异常(第6步);当命令因为断路器跳闸而短路时(第4步);当该命令的线程池队列或信号量满时(第5步),或者当命令执行时长超过阀值时。

编写回调逻辑提供一个通用的响应,以便在不存在任何网络依赖项或者所有依赖项都失效时,能够从内存中的缓存中或其他静态逻辑中获取一个通用的响应(不至于在单个或多个依赖项失效时,产生连锁反应带崩整个应用)。如果必须在回调逻辑中使用网络请求,应该通过另一个HystrixCommand 或 HystrixObservableCommand 来完成。

在 HystrixCommand 中,在 HystrixCommand.getFallback()方法中提供自定义的回调逻辑,方法返回单个回调值。

在 HystrixObservableCommand 中,在HystrixObservableCommand.resumeWithFallback() 方法中提供自定义的回调逻辑,方法返回一个 可传播回调值的 Observable。

如果回调方法返回了响应,Hystrix 会将该响应返回给调用者。在 HystrixCommand 中,Hystrix 返回给调用者一个可传播响应值的 Observable;在 HystrixObservableCommand 中,Hystrix 将直接返回resumeWithFallback()返回的 Observable 对象。

如果没有为 Hystrix 的命令提供回调逻辑,或者回调逻辑会抛出异常,Hystrix 仍然会返回一个 Observable 对象,但是没有传播值并且会立即终止传播,并发送一个onError通知。正是这个onError的通知会将导致命令失败的异常信息返回给调用者。(用户提供可能会失败的回调逻辑是一个很不好的做法,用户应该提供没有任何可能执行失败的回调逻辑)

根据调用 Hystrix 命令方式的不同,失败时或不存在依赖项时的回调结果也会有所不同:

  • execute()——抛出异常
  • queue()——成功时返回java.util.concurrent.Future,但如果调用 Future.get()将抛出异常
  • observe()——返回 Observable 对象,当你订阅该 Observable 时,将会立即终止并且调用订阅者的onError方法
  • toObservable()——同observe()

9. 返回成功的 Response

如果 Hystrix 命令执行成功,那么将以 Observable 的形式返回单个响应或多个响应给调用者。取决于上面第2步执行命令的方式不同,返回的 Observable 在返回给你之前会通过以下方式传播:
Observable Transformed
- execute()——和queue()获取的方式一样获取一个 Future,然后通过Future.get()方法通过 Obsevable 获取返回的单个值
- queue()——将 Observable 转换成 BlockingObservable,以便可以转换成Future并返回
- observe()——快速订阅返回的 Observable 并开始执行订阅命令的流程;返回 Observable,当被订阅时可以重新传播和重新通知
- toObservable()——返回相同的 Observable;用户必须订阅它才能真正开始执行订阅命令的流程

猜你喜欢

转载自blog.csdn.net/a58yyxg/article/details/80242534