官网

官网

官网图示

1610803433191.png

步骤说明

1610803433336.png

创建HystrixCommand 或者HystrixObservableCommand

  在使用Hystrix的过程中,会对依赖服务的调用请求封装成命令对象,Hystrix 对 命令对象抽象了两个抽象类:HystrixCommand 和HystrixObservableCommand 。

HystrixCommand 表示的命令对象会返回一个唯一返回值:

  1. public class QueryOrderCommand extends HystrixCommand<Order> {
  2. private String orderId;
  3. public QueryOrderCommand(String orderId){
  4. super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("hystrix-order-group"))
  5. .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("hystrix-thread-order"))
  6. .andCommandKey(HystrixCommandKey.Factory.asKey("hystrix-pay-order"))
  7. .andCommandPropertiesDefaults(HystrixCommandProperties.defaultSetter())
  8. .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.defaultSetter()
  9. .withCoreSize(10)
  10. .withQueueSizeRejectionThreshold(15)
  11. )
  12. );
  13. this.orderId = orderId;
  14. }
  15. @Override
  16. protected Order run() throws Exception {
  17. System.out.println("fetching order info via service call");
  18. return new Order();
  19. }
  20. }
  21. class Order{
  22. private String orderId;
  23. private String productId;
  24. private String status;
  25. }

HystrixObservableCommand 表示的命令对象 会返回多个返回值:

执行命令

Hystrix中共有4种方式执行命令,如下所示:

1610803433370.png

这四种命令中,exeucte()、queue()、observe()的表示也是通过toObservable()实现的,其转换关系如下图所示:

1610803433397.png

  1. K value = command.execute();
  2. // 等价语句:
  3. K value = command.execute().queue().get();
  4. Future<K> fValue = command.queue();
  5. //等价语句:
  6. Future<K> fValue = command.toObservable().toBlocking().toFuture();
  7. Observable<K> ohValue = command.observe(); //hot observable,立刻订阅,命令立刻执行
  8. //等价语句:
  9. Observable<K> ohValue = command.toObservable().subscribe(subject);
  10. // 上述执行最终实现还是基于`toObservable()`
  11. Observable<K> ocValue = command.toObservable(); //cold observable,延后订阅,订阅发生后,执行才真正执行

返回结果是否被缓存?

  如果当前命令对象配置了允许从结果缓存中取返回结果,并且在结果缓存中已经缓存了请求结果,则缓存的请求结果会立刻通过Observable的格式返回。具体Hystrix的缓存策略,请参考``

断路器是否打开?

如果第3步没有缓存没有命中,则判断一下当前断路器的断路状态是否打开。如果断路器状态为打开状态,则Hystrix将不会执行此Command命令,直接执行步骤8 调用Fallback;

如果断路器状态是关闭,则执行 步骤5 检查是否有足够的资源运行 Command命令

资源(线程池/队列/信号量)是否已满?

如果当前要执行的Command命令 先关连的线程池 和队列(或者信号量)资源已经满了,Hystrix将不会运行 Command命令,直接执行 步骤8的Fallback降级处理;如果未满,表示有剩余的资源执行Command命令,则执行步骤6

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

当经过步骤5 判断,有足够的资源执行Command命令时,本步骤将调用Command命令运行方法,基于不同类型的Command,有如下两种两种运行方式:

1610803433419.png

如果run() 或者construct()方法 的真实执行时间超过了Command设置的超时时间阈值, 则当前则执行线程(或者是独立的定时器线程)将会抛出TimeoutException。抛出超时异常TimeoutException,后,将执行步骤8的Fallback降级处理。即使run()或者construct()执行没有被取消或中断,最终能够处理返回结果,但在降级处理逻辑中,将会抛弃run()或construct()方法的返回结果,而返回Fallback降级处理结果。

注意事项 需要注意的是,Hystrix无法强制 将正在运行的线程停止掉—Hystrix能够做的最好的方式就是在JVM中抛出一个InterruptedException。如果Hystrix包装的工作不抛出中断异常InterruptedException, 则在Hystrix线程池中的线程将会继续执行,尽管调用的客户端已经接收到了TimeoutException。这种方式会使Hystrix 的线程池处于饱和状态。大部分的Java Http Client 开源库并不会解析 InterruptedException。所以确认HTTP client 相关的连接和读/写相关的超时时间设置。 如果Command命令没有抛出任何异常,并且有返回结果,则Hystrix将会在做完日志记录和统计之后会将结果返回。 如果是通过run()方式运行,则返回一个Obserable对象,包含一个唯一值,并且发送一个onCompleted通知;如果是通过consturct()方式运行 ,则返回一个Observable对象。

计算断路器的健康状况

Hystrix 会统计Command命令执行执行过程中的成功数、失败数、拒绝数和超时数,将这些信息记录到断路器(Circuit Breaker)中。断路器将上述统计按照时间窗的形式记录到一个定长数组中。断路器根据时间窗内的统计数据去判定请求什么时候可以被熔断,熔断后,在接下来一段恢复周期内,相同的请求过来后会直接被熔断。当再次校验,如果健康监测通过后,熔断开关将会被关闭。

获取Fallback

当以下场景出现后,Hystrix将会尝试触发Fallback:

步骤6 Command执行时抛出了任何异常; 步骤4 断路器已经被打开 步骤5 执行命令的线程池、队列或者信号量资源已满 命令执行的时间超过阈值

返回成功结果

如果 Hystrix 命令对象执行成功,将会返回结果,或者以Observable形式包装的结果。根据步骤2的command 调用方式,返回的Observable 会按照如下图说是的转换关系进行返回:

1610803433450.png

断路器工作原理

1610803433482.png

  1. 断路器时间窗内的请求数 是否超过了请求数断路器生效阈值circuitBreaker.requestVolumeThreshold,如果超过了阈值,则将会触发断路,断路状态为开启
    例如,如果当前阈值设置的是20,则当时间窗内统计的请求数共计19个,即使19个全部失败了,都不会触发断路器。
  2. 并且请求错误率超过了请求错误率阈值errorThresholdPercentage
  3. 如果两个都满足,则将断路器由关闭迁移到开启
  4. 如果断路器开启,则后续的所有相同请求将会被断路掉;
  5. 直到过了沉睡时间窗sleepWindowInMilliseconds后,再发起请求时,允许其通过(此时的状态为半开起状态)。如果请求失败了,则保持断路器状态为开启状态,并更新沉睡时间窗。如果请求成功了,则将断路器状态改为关闭状态;

核心的逻辑如下:

  1. @Override
  2. public void onNext(HealthCounts hc) {
  3. // check if we are past the statisticalWindowVolumeThreshold
  4. if (hc.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
  5. // we are not past the minimum volume threshold for the stat window,
  6. // so no change to circuit status.
  7. // if it was CLOSED, it stays CLOSED
  8. // if it was half-open, we need to wait for a successful command execution
  9. // if it was open, we need to wait for sleep window to elapse
  10. } else {
  11. if (hc.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
  12. //we are not past the minimum error threshold for the stat window,
  13. // so no change to circuit status.
  14. // if it was CLOSED, it stays CLOSED
  15. // if it was half-open, we need to wait for a successful command execution
  16. // if it was open, we need to wait for sleep window to elapse
  17. } else {
  18. // our failure rate is too high, we need to set the state to OPEN
  19. if (status.compareAndSet(Status.CLOSED, Status.OPEN)) {
  20. circuitOpened.set(System.currentTimeMillis());
  21. }
  22. }
  23. }
  24. }

断路器相关配置:

1610803433509.png

Key值的配置问题 默认配置:上述Key值之前要加上hystrix.command.default.前缀拼接 实例配置:上述Key值之前要加上hystrix.command.. 前缀拼接