一、背景
1. 当前问题
一个系统,所有请求共用同一个APP容器(Tomcat/jetty/等),共用一个用户线程池,依赖多个不同的远程服务。
当系统健康时,处理请求的延时较低,服务正常运行;当某个后端依赖项变得延迟,会导致处理该请求的用户线程长时间阻塞。在流量较低时,只会影响请求本身;在高流量的情况下,单个后端依赖项的延迟可能会导致服务的所有用户线程都在阻塞等待。
这个问题不仅导致与该后端依赖项有关的请求没办法被正常处理,还会导致其他请求拿不到所需资源而出现异常,从而导致整个系统都没办法正常运行。
对于上述问题,需要通过手段对故障、延迟进行隔离和管理,以便单个失败的依赖项不会导致整个应用程序或系统崩溃。



2. Hystrix简介
Hystrix是Netflix开源的容错库,旨在处理分布式系统中的故障和延迟。它通过实现断路器模式、请求缓存、请求合并等功能,提供了弹性和可靠的解决方案。Hystrix能够在服务之间进行隔离,当一个服务出现故障时,它可以防止故障的扩散,提高系统的可用性和稳定性。在微服务架构中,一个请求需要调用多个服务是非常常见的,较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阈值(Hystrix是5秒20次)断路器将会被打开。断路打开后,可以避免连锁故障,通过fallback方法快速返回一个值。
3. Hystrix的目标
Hystrix旨在实现以下目标:
- 保护并控制通过第三方客户端库访问(通常是通过网络)的依赖项的延迟和故障。
- 阻止复杂分布式系统中的级联故障。
- 快速失败并快速恢复。
- 在可能的情况下回退并优雅降级。
- 启用近乎实时的监控、警报和操作控制。
4. Hystrix的功能
为了实现上述目标,Hystrix提供了以下功能:
- 用
HystrixCommand或HystrixObservableCommand对象包装对所有外部系统(或“依赖项”)的调用,通常在单独的线程中执行 - 自定义调用依赖项的超时时间
- 为每个依赖项维护一个小的线程池(或信号量);如果它变满,将要发送到该依赖项的请求立即被拒绝,而不是排队等待
- 统计成功、失败(客户端抛出的异常)、超时和线程拒绝的数量
- 可通过手动或自动的方式,开启断路器以停止一段时间内对特定服务的所有请求;当统计的失败率超过阈值,断路器会自动开启
- 在请求失败、被拒绝、超时或短路时执行fallback逻辑
- 近实时地监控指标和配置更改

二、原理介绍
1. Hystrix执行流程
下图展示了通过Hystrix请求远程服务的流程

1.1 创建HystrixCommand/HystrixObservableCommand对象
第1步,创建一个HystrixCommand或HystrixObservableCommand包装远程调用依赖的过程。HystrixCommand和HystrixObservableCommand的区别后续介绍。
1.2 执行Command
第2步,执行第一步创建的命令Command,可以使用以下四种方式:execute()、queue()、observe()、toObservable(),区别后续介绍
1.3 判断是否有请求缓存
第3步,判断是否有请求缓存,若存在请求缓存则直接将缓存的值返回,请求缓存的用法后续介绍。
1.4 判断是否开启断路
第4步,判断断路器是否开启,开启则不能执行远程调用的操作,直接到第8步;若未开启,则到第5步。第4步的断路器开启与否,是通过第7步的断路器健康值判断的。
1.5 判断线程池/信号量是否已满
第5步,如果与命令关联的线程池和队列(或信号量)已满,那么Hystrix将不会执行该命令,而是立即将流量路由到(8)获取Fallback。第5步的结果会反馈到第7步的断路器健康值中。
1.6 执行远程调用
第6步,执行远程调用的方法。在第1步构造的HystrixCommand或HystrixObservableCommand中,会重写HystrixCommand.run()和HystrixObservableCommand.construct(),里面执行远程调用过程。
如果执行失败/超时,则会路由到第8步执行fallback方法。
值得注意的是,执行超时时,线程隔离策略下定时器会抛出超时异常,并且通知执行任务的线程中断;信号量隔离策略下,定时器会抛出超时异常,但是执行任务的线程会执行到任务结束。
1.7 计算断路器的健康度
第7步,Hystrix向断路器报告成功、失败、拒绝和超时,断路器维护一组滚动计数器来计算统计数据。
它使用这些统计数据来确定何时应该断路,此时它会拒绝任何后续请求,直到恢复期过去,然后它会让会进入半开状态。在此状态下,下一个请求将尝试调用后端服务。如果该请求成功,断路器会认为服务已经恢复并转回 关闭 状态;如果请求仍然失败,断路器会再次回到 打开 状态,继续进行短路操作。
1.8 获取并执行fallback
第8步,当第4步发现断路器打开、第5步线程池/信号量已满、第6步执行异常/超时,Hystrix就会尝试获取Command中自定义的备用方法getFallback()。建议该方法不依赖任何网络连接,而是从内存缓存或其他静态逻辑中获取。
如果fallback执行异常或者没有重写getFallback()方法,则会抛出异常。
1.9 返回成功的响应
如果执行成功,则会将远程调用的结果返回给调用者。
2. 断路器工作流程
下图展示了HystrixCommand和断路器的交互流程以及断路器的工作流程:
- 当执行HystrixCommand时,会调用断路器查询是否允许请求。断路器会查询断路器是否开启,若没开启,直接返回允许请求;若断路器已开启,会判断恢复时间是否已过,已过允许1个请求,未过返回不允许请求。(对应2.1的第4步)
- 判断断路器是否开启的方式时,计算时间范围内错误百分比是否超过阈值,如果超过阈值,则返回已开启。
- 当执行HystrixCommand之后,会将结果反馈给断路器,以更新断路器的健康度。

3、 隔离策略
Hystrix 使用舱壁模式来隔离依赖关系,并限制对任何单一依赖关系的并发访问。

3.1 线程池隔离(THREAD)
在此策略中,每个服务调用都在独立的线程中执行。线程隔离可以防止服务调用时间过长,导致其他服务调用受到影响。当一个服务调用超时或发生故障时,它不会影响到其他服务调用。线程隔离可以确保服务之间的独立性,提高系统的稳定性。


3.1.1 优势
- 该应用程序完全不受失控客户端库的影响。给定依赖库的池可以填满,而不会影响应用程序的其余部分。
- 支持任务排队
- 支持超时中断
- 支持异步调用
3.1.2 劣势
- 线程调用会产生额外的开销
3.1.3 适用场景
- 对于可能耗时较长、网络延迟较高的外部服务调用。
- 当需要确保每个命令在独立的线程上运行以防止阻塞主线程。
- 为了防止故障传播和资源耗尽,需要对每个命令进行严格的资源限制。
- 能够承受一定的线程创建和销毁的开销,以换取更稳定的系统行为。
- 适用于处理高负载和突发流量的情况,因为线程池可以帮助稳定系统的整体性能。
3.2 信号量隔离(SEMAPHORE)
信号量隔离是一种基于计数器的轻量级隔离方法,它不创建新的线程。相反,Hystrix 使用一个指定大小的信号量来控制并发访问的数量。一旦达到最大值,任何额外的请求都将被拒绝并触发降级策略。
3.2.1 优势
- 轻量,无额外开销
3.2.2 劣势
- 不支持任务排队
- 不支持超时中断。执行超时,执行线程会继续阻塞,直到底层的网络调用超时
- 不支持异步调用。实际上还是所有请求共用用户线程,没办法完全隔离。
3.2.3 适用场景
- 对性能敏感或低延迟要求的应用。
- 当调用的服务通常是快速响应的,例如查询缓存服务或者内部服务间通信。
- 调用的依赖服务不需要消耗大量的 CPU 或者 IO 资源。
- 需要严格控制并发请求的数量,但又不希望引入额外的线程开销。
- 在某些情况下,可以用于轻量级的微服务之间的一致性保证。
三. 使用介绍
本章节将介绍hystrix提供的原始API、Spring Cloud集成Hystrix的使用
1. Hystrix使用
1.1 依赖
<!-- https://mvnrepository.com/artifact/com.netflix.hystrix/hystrix-core -->
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.18</version>
</dependency>
1.2 Hello World
下面是创建一个HystrixCommand的方式,继承HystrixCommand,泛型为远程调用响应的类型。
构造方法中指定了该命令所属的分组key,Hystrix会基于分组key用于报告、告警、仪表盘、权限控制等,并且在不指定线程池的情况下,会根据这个key命名线程池。(也就是说,相同key共用同一个线程池)
run()方法中,是真正执行远程调用的位置。
public class CommandHelloWorld extends HystrixCommand<String> {
private final String name;
public CommandHelloWorld(String name) {
// 指定命令分组为ExampleGroup
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected String run() {
// 真正执行远程调用的位置
return "Hello " + name + "!";
}
}
执行HystrixCommand,同步等待结果
String s = new CommandHelloWorld("World").execute();
执行HystrixCommand,异步等待结果。
Future<String> fs = new CommandHelloWorld("World").queue();
String s = fs.get();
响应式执行HystrixCommand,observe()将HystrixCommand转换为Observable,并通过subscribe()设置订阅者,设置当处理结果、处理异常、出现新元素时的行为。
Observable<String> ho = new CommandHelloWorld("World").observe();
ho.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
LOGGER.info("Completed.");
}
@Override
public void onError

本文介绍了Netflix开源的容错库Hystrix,它用于处理分布式系统中的故障和延迟。阐述了Hystrix的背景、目标、功能,详细讲解其执行流程、断路器工作流程和隔离策略,还介绍了在原生及Spring Cloud中的使用方法、参数配置和常见模式,以提高系统可用性和稳定性。
最低0.47元/天 解锁文章
529

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



