Hystrix 原理
问题
分布式架构中如果一个服务节点有问题可能会影响到整个用户请求
设计原则
- 防止任何单个依赖耗尽容器的用户线程
- 通过减少负载和快速失败替代排队
- 可行的情况下进行回退,避免用户失败
- 使用隔离技术(例如隔板、泳道和断路器模式)来限制任何一种依赖性的影响。
- 通过近乎实时的指标、监控和警报优化发现时间
- 防止整个依赖客户端执行中的故障,而不仅仅是网络流量
- 实时修改配置
如何完成的这些目标
- 对调用进行包装,在一个隔离的线程中执行
- 自定义调用超时时间
- 为每个依赖项维护一个小的线程池(或信号量)如果它已满,则发往该依赖项的请求将立即被拒绝,而不是排队
- 跟踪成功、失败(客户端抛出的异常)、超时和线程拒绝
- 如果服务的错误百分比超过阈值,则手动或自动触发断路器以在一段时间内停止对特定服务的所有请求
- 当请求失败、被拒绝、超时或短路时执行回退逻辑
- 近乎实时地监控指标和配置更改
请求过程
- 构造
HystrixCommand
或者HystrixObservableCommand
- 执行命令
HystrixCommand 用在依赖服务返回一个操作结果的时候
-execute():同步执行。从依赖的服务返回一个单一的结果对象,或是在发生错误的时候抛出异常。
-queue();异步执行。直接返回一个Future对象,其中包含了服务执行结束时要返回的单一结果对象。
HystrixObservableCommand 用在依赖服务返回多个操作结果的时候。它也实现了两种执行方式
-observe():返回Obervable对象,他代表了操作的多个结果,他是一个HotObservable
-toObservable():同样返回Observable对象,也代表了操作多个结果,但它返回的是一个Cold Observable。
- 是否有缓存
- 断路器是否打开
- 判断
Pool/Queue/Semaphore
是否已满
如果与该命令关联的线程池和队列(或信号量,如果不在线程中运行)已满,则 Hystrix 将不会执行该命令,但会立即将流路由到
Get the Fallback
- 执行请求
- 如果方法超时则抛出
TimeoutException
异常,在这种情况下,Hystrix
通过8.Get the Fallback
路由响应 - 请注意,没有办法强制潜在线程停止工作——
Hystrix
在 JVM 上可以做的最好的事情就是抛出一个InterruptedException
。 如果Hystrix
包装的工作不遵守InterruptedExceptions
,Hystrix
线程池中的线程将继续其工作,尽管客户端已经收到TimeoutException
。 这种行为可以使Hystrix
线程池饱和,尽管负载被“正确卸载”。大多数 Java HTTP 客户端库不解释InterruptedExceptions
。 因此,请确保在 HTTP 客户端上正确配置连接和读/写超时。 - 如果该命令没有抛出任何异常并且返回了响应,则
Hystrix
在执行一些日志记录和指标报告后返回此响应。
- 断路器检查
Hystrix
将成功、失败、拒绝和超时报告给断路器,断路器维护一组滚动计算统计数据的计数器。
它使用这些统计信息来确定电路何时应该“跳闸”,此时它会短路任何后续请求,直到恢复期结束,然后在首先检查某些健康检查后再次关闭电路。
- 返回结果
断路器
- 假如我们设置了一个断路器打开的阀值
- 假如错误请求数量超过了这个阀值
- 断路器的状态会从
CLOSED
变成OPEN
- 当它打开时,它会短路针对该断路器的所有请求
- 进入半开状态,(HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()),让下一个请求通过(这是半开状态)。 如果请求失败,断路器将在休眠窗口期间返回 OPEN 状态。 如果请求成功,断路器将转换为 CLOSED,并且 1. 中的逻辑再次接管
熔断监控的时间区间
metric.tollingStats.timeInMilliseconds
,整个熔断过程将实时统计每个时间区间内的监控数据,并计算该时间区间内的错误率
桶数量是固定的,单个时间区间 = 监控的时间区间 / 桶的数量
如果,当前桶已经满了,当需要进入下一个时间区间时,就把最早的一个桶淘汰掉(先入先出的队列)
请求缓存
请求缓存不是只写入一次结果就不再变化的,而是每次请求到达
Controller
的时候,我们都需要为HystrixRequestContext
进行初始化,之前的缓存也就是不存在了,我们是在同一个请求中保证结果相同,同一次请求中的第一次访问后对结果进行缓存,缓存的生命周期只有一次请求!