Hystrix原理分析
文章目录
一:熔断,降级,限流,隔离
面对高并发的流量,我们通常会使用四种方式(熔断&降级&限流&隔离)来防止瞬时大流量对系统的冲击
1:何为熔断
关键字:断路保护。
比如 A 服务调用 B 服务,由于网络问题或 B 服务宕机了或 B 服务的处理时间长,导致请求的时间超长,如果在一定时间内多次出现这种情况,就可以直接将 B 断路了(A 不再请求B)。
而调用 B 服务的请求直接返回降级数据,不必等待 B 服务的执行。因此 B 服务的问题,不会级联影响到 A 服务

2:何为降级
关键字:返回降级数据。
网站处于流量高峰期,服务器压力剧增,根据当前业务情况及流量,对一些服务和页面进行有策略的降级(停止服务,所有的调用直接返回降级数据)。
以此缓解服务器资源的压力,保证核心业务的正常运行,保持了客户和大部分客户得到正确的响应。
降级数据可以简单理解为快速返回了一个 false,前端页面告诉用户“服务器当前正忙,请稍后再试。”

熔断和降级的相同点?
- 熔断和限流都是为了保证集群大部分服务的可用性和可靠性。防止核心服务崩溃。
- 给终端用户的感受就是某个功能不可用。
熔断和降级的不同点?
- 熔断是被调用方出现了故障,主动触发的操作。
- 降级是基于全局考虑,停止某些正常服务,释放资源。
3:何为限流
对请求的流量进行控制, 只放行部分请求,使服务能够承担不超过自己能力的流量压力

二:Hystrix
Hystrix:高可用性保障的一个框架。由 Netflix 出品(Netflix可以理解为国内的爱奇艺等视频网站)
- 2011 年,其中的 API 团队为了提升系统的可用性和稳定性,发展出了 Hystrix 框架。
- 2012 年,Hystrix 区域比较成熟稳定。其他团队也开始使用 Hystrix。
- 2018 年 11 月,Hystrix在其 Github 主页宣布,不再开放新功能,推荐开发者使用其他仍然活跃的开源项目。
Hystrix设计理念如下:
- 阻止服务的雪崩效应。
- 快速失败和快速恢复。
- 优雅降级。
- 使用资源隔离技术,如 bulkhead(舱壁隔离技术)、swimlane(泳道技术)、circuit breaker(断路技术)。
- 近实时的监控、报警及运维操作。
1:线程池隔离技术
使用线程池隔离,比如说有 3 个服务 A、B、C,每个服务的线程池分配 10,20,30个线程,当 A 服务线程池中的 10 个线程都拿出来使用后,如果调用服务 A 的请求量增加,还想再增加线程是不行的,因为 A 服务分配的线程已经用完了,不会拿其他的服务的线程,这样就不会影响其他服务了。
Hystrix 默认使用线程池隔离模式。

线程池隔离技术优点
- 即使自己的此案成池满了,也不会影响任何其他其他的服务调用。
- 线程池的健康状态会上报,可以近实时修改依赖服务的调用配置。
- 线程池具有异步特性,可以构建一层异步调用层。
- 具有超时检测的机制,尤其在服务间调用特别有用。
线程池隔离技术缺点
- 线程池本身就会带来一些问题,比如线程切换,线程管理,无疑增加了 CPU 的开销。
- 如果线程池中的线程利用率很低,则无疑是一种浪费。
2:信号量隔离技术
简单来说就是一个池子里面放着一定数量的信号量
服务 A 每次调用服务 B 之前,需要从池子里面申请信号量,申请到了,才能去调用 B 服务

线程池隔离和信号量的场景对比
- 线程池隔离技术,适合大部分场景,但需要设置服务的超时时间。
- 信号量隔离技术,适合内部比较复杂的业务,不涉及网络请求问题。
3:断路器工作原理
断路器时间窗内的请求数是否超过了请求数断路器生效阈值circuitBreaker.requestVolumeThreshold
如果超过了阈值,则将会触发断路,断路状态为开启
如果当前阈值设置的是20,则当时间窗内统计的请求数共计19个,即使19个全部失败了,都不会触发断路器。
并且请求错误率超过了请求错误率阈值errorThresholdPercentage
如果两个都满足,则将断路器由关闭迁移到开启
// 请求数 >= 生效阈值 并且 错误率 > 设置的错误率
if (requestCount >= circuitBreaker.requestVolumeThreshold && errorRate > errorThresholdPercentage) {
flag = true; // 将断路器由关闭迁移到开启
// 断路计时器开始计时
}
// 后续的请求直接断路掉
// 计时器计时长 > sleepWindowInMilliseconds -> 半开状态 -> 再次请求,允许调用底层服务
// 1:如果请求失败了,则保持断路器状态为开启状态,并更新沉睡时间窗
// 2:如果请求成功了,则将断路器状态改为关闭状态
如果断路器开启,则后续的所有相同请求将会被断路掉;
直到过了沉睡时间窗sleepWindowInMilliseconds后,再发起请求时,允许其通过(此时的状态为半开起状态)。如果请求失败了,则保持断路器状态为开启状态,并更新沉睡时间窗。如果请求成功了,则将断路器状态改为关闭状态;
4:Hystrix工作原理(三态)
Hystrix的工作流程上大概会有如下9个步骤

4.1:两大抽象类
在使用Hystrix的过程中,会对依赖服务的调用请求封装成命令对象
Hystrix 对 命令对象抽象了两个抽象类:HystrixCommand 和 HystrixObservableCommand 。
HystrixCommand 表示的命令对象会返回一个唯一返回值
HystrixObservableCommand 表示的命令对象 会返回多个返回值
public class QueryOrderCommand extends HystrixCommand<Order> {
private String orderId;
public QueryOrderCommand(String orderId){
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("hystrix-order-group"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("hystrix-thread-order"))
.andCommandKey(HystrixCommandKey.Factory.asKey("hystrix-pay-order"))
.andCommandPropertiesDefaults(HystrixCommandProperties.defaultSetter())
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.defaultSetter()
.withCoreSize(10)
.withQueueSizeRejectionThreshold(15)
)
);
this.orderId = orderId;
}
@Override
protected Order run() throws Exception {
System.out.println("fetching order info via service call");
return new Order();
}
}
class Order{
private String orderId;
private String productId;
private String status;
}
4.2:执行命令
Hystrix中共有4种方式执行命令,如下所示:
| 执行方式 | 说明 | 可用对象 |
|---|---|---|
| execute() | 阻塞式同步执行,返回依赖服务的单一返回结果(或者抛出异常) | HystrixCommand |
| queue() | 基于Future的异步方式执行,返回依赖服务的单一返回结果(或者抛出异常) | HystrixCommand |
| observe() | 基于Rxjava的Observable方式,返回通过Observable表示的依赖服务返回结果 代调用代码先执行(Hot Obserable) | HystrixObservableCommand |
| toObservable() | 基于Rxjava的Observable方式,返回通过Observable表示的依赖服务返回结果 执行代码等到真正订阅的时候才会执行(cold observable) | HystrixObservableCommand |
这四种命令中,exeucte()、queue()、observe()的表示也是通过toObservable()实现的,其转换关系如下图所示:

K value = command.execute();
// 等价语句:
K value = command.execute().queue().get();
Future<K> fValue = command.queue();
//等价语句:
Future<K> fValue = command.toObservable().toBlocking().toFuture();
Observable<K> ohValue = command.observe(); //hot observable,立刻订阅,命令立刻执行
//等价语句:
Observable<K> ohValue = command.toObservable().subscribe(subject);
// 上述执行最终实现还是基于`toObservable()`
Observable<K> ocValue = command.toObservable(); // cold observable,延后订阅,订阅发生后,执行才真正执行
4.3:返回结果是否被缓存
如果当前命令对象配置了允许从结果缓存中取返回结果,并且在结果缓存中已经缓存了请求结果,则缓存的请求结果会立刻通过Observable的格式返回。
4.4:断路器是否打开
如果第3步没有缓存没有命中,则判断一下当前断路器的断路状态是否打开:
- 如果断路器状态为打开状态,则Hystrix将不会执行此 Command 命令,直接执行步骤8 调用Fallback;
- 如果断路器状态是关闭,则执行 步骤5 检查是否有足够的资源运行 Command 命令
4.5:资源是否已满
这里的资源指的是和Command命令 先关连的线程池 和队列(或者信号量)等资源。
- 如果当前要执行的资源已经满了,Hystrix将不会运行 Command命令,直接执行 步骤8的Fallback降级处理;
- 如果未满,表示有剩余的资源执行Command命令,则执行步骤6
4.6:run command
当经过步骤5 判断,有足够的资源执行Command命令时,本步骤将调用Command命令运行方法
基于不同类型的Command,有如下两种两种运行方式:
| 运行方式 | 说明 |
|---|---|
HystrixCommand.run() | 返回一个处理结果或者抛出一个异常 |
HystrixObservableCommand.construct() | 返回一个Observable表示的结果(可能多个),或者 基于onError的错误通知 |
如果运行方法的真实执行时间超过了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对象。
4.7:计算断路器的健康状况
Hystrix 会统计Command命令执行执行过程中的成功数、失败数、拒绝数和超时数,将这些信息记录到断路器(Circuit Breaker)中。
断路器将上述统计按照时间窗的形式记录到一个定长数组中。
断路器根据时间窗内的统计数据去判定请求什么时候可以被熔断,熔断后,在接下来一段恢复周期内,相同的请求过来后会直接被熔断。
当再次校验,如果健康监测通过后,熔断开关将会被关闭。
4.8:获取Fallback(降级四大条件)
当以下场景出现后,Hystrix将会尝试触发Fallback:(触发服务降级的四个情况)
- 步骤6 Command run时抛出了任何异常;
- 步骤4 断路器已经被打开[已经是服务熔断状态]
- 步骤5 执行命令的线程池、队列或者信号量资源已满
- 命令执行的时间超过阈值
4.9:返回成功的结果
如果 Hystrix 命令对象执行成功,将会返回结果,或者以Observable形式包装的结果。
根据步骤2的command 调用方式,返回的Observable 会按照如下图说是的转换关系进行返回:

三:简单说一下Sentinel
下载地址:https://github.com/alibaba/Sentinel/releases
Sentinel:面向分布式服务架构的流量控制组件
主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性
- 2012 年,Sentinel 诞生,主要功能为入口流量控制。
- 2013-2017 年,Sentinel 在阿里巴巴集团内部迅速发展,成为基础技术模块,覆盖了所有的核心场景。
- 2018 年,Sentinel 开源,并持续演进。
- 2019 年,Sentinel 朝着多语言扩展的方向不断探索,推出 C++ 原生版本,同时针对 Service Mesh 场景也推出了 Envoy 集群流量控制支持,以解决 Service Mesh 架构下多语言限流的问题。
- 2020 年,推出 Sentinel Go 版本,继续朝着云原生方向演进
sentinel特征如下
- 丰富的应用场景。 支撑阿里的双十一核心场景,如秒杀、消息削峰填谷、集群流量控制、实时熔断下游不可用。
- 完备的实时监控。 可以看到接入应用的单台机器秒级数据,以及集群的汇总情况。
- 广泛的开源生态。 Spring Cloud、Dubbo、gRPC 都可以接入 Sentinel。
- 完善的SPI扩展点。 实现扩展接口来快速定制逻辑。

sentinel的构成
核心库不依赖任何框架/库,能在所有的 Java 运行时环境运行,且对 Spring Cloud、Dubbo 等框架有较好的支持。
控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器
资源
Sentinel 中的资源是核心概念,可以是 Java 应用程序中的任何内容,可以是提供的服务,甚至是一段代码。
可以通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel保护起来。可以如下几种方式来标识资源:
- 方法签名。
- URL。
- 服务名称等
1:设计理念
Sentinel 作为一个流量控制器,可以根据需要把随机的请求调整成合适的形状,如下图所示:

流控功能
Sentinel 可以通过并发线程数模式的流量控制来提供信号量隔离的功能
而且它还具备响应时间的熔断降级模式,防止过多的慢调用占满并发数而影响整个系统
熔断功能
Sentinel 和 Hystrix 都是基于熔断器模式。
都支持基于异常比率来进行熔断,但 Sentinel 更强大,可以基于响应时间、异常比率和异常数来进行熔断降级
实时统计
Sentinel 和 Hystrix 都是基于滑动窗口进行实时统计
Hystrix 是基于 RxJava 的事件驱动模型,在服务调用成功/失败/超时的时候发布响应的事件,通过一系列的变换和聚合最终得到实时的指标统计数据流,可以被熔断器或 Dashboard 消费。
而 Sentinel 是基于 LeapArray 的滑动窗口
2:突出特性
除了上面提到的三大对比外,Sentinel 还有一些 Hystrix 不具备的功能
2.1:流量控制
原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
Sentinel 可以基于QPS/并发数进行流量控制,也可以基于调用关系进行流量控制。
基于 QPS 进行流量控制有以下几种方式
-
直接拒绝:当QPS 超过一定阈值时,直接拒绝。适用于对系统处理能力确切已知的情况。
-
慢启动预热:当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮
-
匀速排队:请求以均匀的速度通过,对应的是漏桶算法。
基于调用关系的流量控制:
- 根据调用方限流。
- 根据调用链路入口限流:链路限流。
- 根据具有关系的资源流量限流:关联流量限流。
2.2:系统自适应限流
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,借助 TCP BBR 思想,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性

我们把系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水
当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;
反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 + 最短处理时间。
- 推论一: 如果我们能够保证水管里的水量,能够让水顺畅的流动,则不会增加排队的请求;也就是说,这个时候的系统负载不会进一步恶化。
- 推论二: 当保持入口的流量是水管出来的流量的最大的值的时候,可以最大利用水管的处理能力
2.3:实时监控和控制面板
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能

2.4:发展和生态
Sentinel 针对 Spring Cloud、Dubbo、gRPC 都进行了适配
引入依赖和简单的配置即可快速接入 Sentinel,相信 Sentinel 将是未来流量防控的一大利器。
四:对比总结
| 功能 | Sentinel | Hystrix |
|---|---|---|
| 隔离策略 | 信号量隔离(并发线程数限制) | 线程池隔离(默)/信号量隔离 |
| 熔断降级策略 | 基于响应时间,异常比例,异常数 | 基于异常比例 |
| 实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(Rxjava) |
| 动态规则配置 | 支持多种数据源 | 支持多种数据源 |
| 扩展性 | 多个扩展点 | 插件的方式 |
| 基于注解的支持 | 支持 @SentinelResource | 支持 @HystrixCommand + @HystrixProperty |
| 限流 | 基于QPS,调用关系的限流 | 有限的限流手段 |
| 流量整形 | 支持预热模式,匀速器模式,预热排队模式 | 不支持 |
| 系统自适应保护 | 支持 | 不支持 |
| 控制台 | 可以配置规则,查看秒级的监控,机器发现 | 简单的监控查看 |
167万+

被折叠的 条评论
为什么被折叠?



