Hystrix是什么
在分布式环境中,许多服务依赖关系中的一些将不可避免地失败。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止它们之间的连锁故障并提供后备选项来实现这一点,所有这些都提高了系统的整体弹性。
-
保护和控制通过第三方客户机库访问依赖项的延迟和故障。
- 在一个复杂的分布式系统中停止一连串的故障。
- 失败快,恢复快。
- 在可能的情况下进行回退并优雅地降级。
- 使接近实时监测,警报和操作控制。
具体做法:
- 将对外部系统(或依赖项)的所有调用包装在HystrixCommand或HystrixObservableCommand对象中,通常在单独的线程中执行。
- 时间长于您定义的阈值的超时调用。有一个默认值,但对于大多数依赖项,您可以通过属性自定义设置这些超时,以便它们比每个依赖项所测得的99.5%的性能略高。
- 为每个依赖项维护一个小的线程池(或信号量);如果该依赖项已满,针对该依赖项的请求将立即被拒绝,而不是排队。
- 度量成功、失败(客户端抛出的异常)、超时和线程拒绝。
- 跳闸断路器以在一段时间内手动或自动地停止对特定服务的所有请求,如果该服务的错误百分比超过阈值。当请求失败、被拒绝、超时或短路时执行回退逻辑。
- 近实时地监视指标和配置更改。
特性
Fallback 服务降级
在服务调用失败时,可以通过 Hystrix 实现 Fallback 服务降级。
熔断器机制
Hystrix 内置断路器 HystrixCircuitBreaker 实现,一共有三种状态:
CLOSED
:关闭OPEN
:打开HALF_OPEN
:半开
其中,断路器处于 OPEN
状态时,链路处于非健康状态,命令执行时,直接调用回退逻辑,跳过正常逻辑。
资源隔离
Hystrix 通过线程池和信号量两种模式来实现隔离。
线程池模式
默认情况下,Hystrix 采用线程池模式来实现隔离。针对调用的每一个服务,给其单独分配一个线程池。例如说,产品服务的调用分配在 A 线程池,用户服务的调用分配在 B 线程池。这样隔离后,两个服务的调用不会相互影响。
信号量模式
使用线程池模式来隔离时,需要进行上下文的切换,带来一定的性能损耗。因此,如果对性能有较高要求,且能接受信号量模式不支持超时的情况,可以考虑采用。
请求缓存
HystrixCommand和HystrixObservableCommand实现了缓存机制,通过指定一个cache key,它们能够在一个请求范围内对运行结果进行缓存以便下次使用。因为缓存是在执行run或者construct方法前进行判断的,所以可以减少run和construct的调用。如果Hystrix没有实现缓存功能,那么每个调用都需要执行construct或者run方法。
请求合并
通过使用HystrixCollapser可以实现合并多个请求批量执行。使用请求合并可以减少线程数和并发连接数,并且不需要使用这额外的工作。请求合并有两种作用域,全局作用域会合并全局内的同一个HystrixCommand请求,请求作用域只会合并同一个请求内的同一个HystrixCommand请求。但是请求合并会增加请求的延时。
流程
-
构造一个HystrixCommand或者HystrixObservableCommand对象
-
执行命令
- execute-阻塞,阻塞直到收到调用的返回值(或者抛出异常)
- queue 返回一个future,可以通过future来获取调用的返回值。
- observe 监听一个调用返回的Observable对象。
- toObservable 返回一个Observable,当监听该Observable后hystrix命令将会执行并返回结果。
同步调用execute本质是调用了queue().get().queue() ,而queue本质上调用了toObservable().toBlocking().toFuture().本质上都是通过rxjava的Observable实现。rxjava是一个通过使用可观察序列组合异步和基于事件的程序的库。
-
是否使用缓存
-
是否开启熔断
-
线程/队列/信号量是否已满
- HystrixObservableCommand.construct() or HystrixCommand.run()
- 如果发送超时,则执行的相应的线程会抛出TimeoutException的异常。然后执行fallback流程,并且丢弃run或者construct的返回值。
- 如果执行方法成功,hystrix会记录日志,上报metrics,然后返回执行结果。
-
熔断器计算
- 执行fallback
- 命令执行失败时;
- run或construct方法抛出异常;
- 熔断器熔断;
- 当线程/队列/信号量已满;
- 当timeout。
- 返回成功结果