当前位置:网站首页>使用 Hystrix 实现微服务的容错处理
使用 Hystrix 实现微服务的容错处理
2022-06-11 10:32:00 【chentian114】
使用 Hystrix 实现微服务的容错处理
文章目录
前言
通过前文《使用 Feign 实现声明式 REST 调用》https://mp.weixin.qq.com/s/npx_w5Sx0NJDumyhfkohLQ ,至此,我们已经用 Eureka 实现了微服务的注册与发现, Ribbon 实现了客户端侧的负载均衡,Feign 实现了声明式的 API 调用。
本文主要讨论如何使用 Hystrix 实现微服务的容错 。
实现容错的手段
如果服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载场景下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃。
当依赖的服务不可用时,服务自身会不会被拖垮?这是我们要考虑的问题。
雪崩效应
微服务架构的应用系统通常包含多个服务层。微服务之间通过网络进行通信,从而支撑起整个应用系统,因此,微服务之间难免存在依赖关系。我们知道,任何微服务都并非 100% 可用,网络往往也很脆弱,因此难免有些请求会失败。
我们常把“基础服务故障”导致“级联故障”的现象称为雪崩效应。雪崩效应描述的是提供者不可用导致消费者不可用,并将不可用逐渐放大的过程。
如何容错
要想防止雪崩效应,必须有一个强大的容错机制。该容错机制需实现以下两点:
- 为网络请求设置超时
- 使用断路器模式
- 如果对某个微服务的请求有大量超时(常常说明该微服务不可用),再去让新的请求访问该服务已经没有任何意义,只会无谓消耗资源。例如,设置了超时时间为 1s ,如果短时间内有大量的请求无法在 1s 内得到响应,就没有必要再去请求依赖的服务了。
- 断路器可理解为对容易导致错误的操作的代理。这种代理能够统计一段时间内调用失败的次数,并决定是正常请求依赖的服务还是直接返回。
- 断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(例如超时),就会在之后的一段时间内,强迫对该服务的调用快速失败,即不再请求所依赖的服务。这样,应用程序就无须再浪费 CPU 时间去等待长时间的超时。
- 断路器也可自动诊断依赖的服务是否已经恢复正常。如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。使用这种方式,就可以实现微服务的“自我修复” – 当依赖的服务不正常时,打开断路器时快速失败,从而防止雪崩效应;当发现依赖的服务恢复正常时,又会恢复请求。
断路器状态转换的逻辑,如下图所示:

- 正常情况下,断路器关闭,可正常请求依赖的服务。
- 当一段时间内,请求失败率达到一定阈值(例如错误率达到 50% ,或 100 次/分钟等),断路器就会打开 此时,不会再去请求依赖的服务。
- 断路器打开一段时间后,会自动进入“半开”状态。此时,断路器可允许一个请求访问依赖的服务。 如果该请求能够调用成功,则关闭断路器;否则继续保持打开状态。
Hystrix 简介
Hystrix 一个实现了超时机制和断路器模式的工具类库。
Hystrix 是由 Netflix 开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。
Hystrix 主要通过以下几点实现延迟和容错:
- 包裹请求:使用 HystrixCommand (或 HystrixObservableCommand )包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
- 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix 可以自动或者手动跳闸,停止请求该服务一段时间。
- 资源隔离:Hystrix 为每个依赖都维护了一个小型的线程池(或者信号量) 。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。
- 监控:Hystrix 可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。
- 回退机制:当请求失败、超时、被拒绝,或当断路器打开时, 执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。
- 自我修复::断路器打开一段时间后,会自动进入“半开”状态。
Hystrix 线程隔离策略
execution.isolation.strategy
Hystrix 的隔离策略有两种:线程隔离和信号量隔离。
- THREAD(线程隔离):使用该方式, HystrixCommand 将在单独的线程上执行,并发请求受到线程池中的线程数量的限制。
- SEMAPHORE (信号量隔离):使用该方式,HystrixCommand 将在调用线程上执行,开销相对较小, 并发请求受到信号量个数的限制。
Hystrix 中默认且推荐使用线程隔离(THREAD) ,因为这种方式有一个除网络超时以外的额外保护层。
一般来说,只有当调用负载非常高时(例如每个实例每秒调用数百次)才需要使用信号量隔离,因为在这种场景下使 THREAD 开销会比较高。信号量隔离一般仅适用于非网络调用的隔离。
可使用 execution.isolation.strategy 属性指定隔离策略。
@HystrixCommand(fallbackMethod = "findByIdFallback", commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")
})
@GetMapping("/{id}")
public User findById(@PathVariable Long id){
//...
}
小结:
- Hystrix 的隔离策略有 THREAD 和 SEMAPHORE 两种,默认是 THREAD。
- 正常情况下,保持默认即可。
- 如果发生找不到上下文的运行时异常,可考虑将隔离策略设置为 SEMAPHORE 。
通用方式整合 Hystrix
复制项目 micro-consumer-movie-ribbon ,将 artifactId 修改为 micro-consumer-movie-ribbon-hystrix 。
添加依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
- 在启动类上添加注解
@EnableCircuitBreaker,为项目启用断路器支持。
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class MicroConsumerMovieHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(MicroConsumerMovieHystrixApplication.class, args);
}
}
- 修改 MovieController ,让其中的 findById 方法具备容错能力。
@HystrixCommand(fallbackMethod = "findByIdFallback")
@GetMapping("/{id}")
public User findById(@PathVariable Long id){
User entity = restTemplate.getForObject("http://micro-provider-user/user/v1/"+id, User.class);
return entity;
}
public User findByIdFallback(Long id){
User user = new User();
user.setId(-1L);
user.setName("默认用户");
return user;
}
为 findById() 方法编写了一个回退方法 findByIdFallback() 该方法与 findById() 方法具有相同的参数与返回值类型,该方法返回了一个默认 User 。
在 findById() 方法上,使用注解 @HystrixCommand(fallbackMethod = "findByIdFallback") fallbackMethod 属性,指定回退方法 findByIdFallback() 。
启动测试。启动 Eureka Server: micro-discovery-eureka 、服务提供者:micro-provider-user 、服务消费者:micro-consumer-movie-ribbon-hystrix ;访问:http://localhost:8010/movie/v1/1 ,正常返回结果。
停止服务提供者:micro-provider-user ,再次访问:http://localhost:8010/movie/v1/1 ,返回回退方法里的默认结果。
当请求失败、被拒绝、超时或者断路器打开时,都会进入回退方法。但进入回退方法并不意味着断路器已经被打开。
如需获得导致 fallback 的原因,只需在 fallback 方法上添加 Throwable 参数即可:
public User findByIdFallback(Long id, Throwable throwable){
logger.error("error:",throwable);
User user = new User();
user.setId(-1L);
user.setName("默认用户");
return user;
}
多数场景下,当发生业务异常时,我们并不想触发 fallback 此时要怎么办呢? Hystrix 有个 HystixBadRequestException 类,这是一个特殊的异常类,当该异常发生时,不会触发回退。因此,可将自定义的业务异常继承该类,从而达到业务异常不回退的效果。
另外,HystrixCommand 为我们提供了 ignoreExceptions 属性,也可借助该属性来配置不想执行回退的异常类。例:@HystrixCommand(fallbackMethod = "findByIdFallback", ignoreExceptions={IllegalArgumentException.class, MyBusinessException.class}) 。
Feign 使用 Hystrix
Spring Cloud 默认已为 Feign 整合了 Hystrix 。
Spring Cloud Dalston 之前的版本中,Feign 默认已开启 Hystrix 支持,无须设置 feign.Hystrix.enabled=true 。
Spring Cloud Dalston 开始, Feign 的 Hystrix 支持,默认关闭,必须设置 feign.Hystrix.enabled=true 属性 。
创建项目。复制项目 micro-consumer-movie-feign ,将 artifactId 修改为 micro-consumer-movie-feign-hystrix-fallback 。
修改 Feign 接口。
@FeignClient(name = "micro-provider-user", fallback = UserFeignClientFallback.class)
public interface UserFeignClient {
@RequestMapping(value = "/user/v1/{id}", method = RequestMethod.GET)
User findById(@PathVariable("id") Long id);
}
@Component
public class UserFeignClientFallback implements UserFeignClient {
@Override
public User findById(Long id) {
User user = new User();
user.setId(-1L);
user.setUsername("默认用户");
return user;
}
}
只需使用 @FeignClient 注解的 fallback 属性,就可为指定名称的 Feign 客户端添加回退。
启动测试。启动 Eureka Server: micro-discovery-eureka 、服务提供者:micro-provider-user 、服务消费者:micro-consumer-movie-feign-hystrix-fallback ;访问:http://localhost:8010/movie/v1/1 ,正常返回结果。
停止服务提供者:micro-provider-user ,再次访问:http://localhost:8010/movie/v1/1 ,返回回退方法里的默认结果。
对于 Feign ,如何获得回退原因呢?可使用注解 @FeignClient 的 fallbackFactory 属性。
创建项目。复制项目 micro-consumer-movie-feign ,将 artifactId 修改为 micro-consumer-movie-feign-hystrix-fallback-factory 。
修改 Feign 接口。
@FeignClient(name = "micro-provider-user", fallbackFactory = UserFeignClientFallbackFactory.class)
public interface UserFeignClient {
@RequestMapping(value = "/user/v1/{id}", method = RequestMethod.GET)
User findById(@PathVariable("id") Long id);
}
@Component
public class UserFeignClientFallbackFactory implements FallbackFactory<UserFeignClient> {
private static final Logger logger = LoggerFactory.getLogger(UserFeignClientFallbackFactory.class);
@Override
public UserFeignClient create(Throwable throwable) {
return new UserFeignClient() {
@Override
public User findById(Long id) {
// 日志最好放在各个 fallback 方法中,而不要直接放在 create 方法中,否则在应用启动时,就会打印该日志
logger.info("fallback; reason was:", throwable);
User user = new User();
user.setId(-1L);
user.setUsername("默认用户");
return user;
}
};
}
}
启动测试。启动 Eureka Server: micro-discovery-eureka 、服务提供者:micro-provider-user 、服务消费者:micro-consumer-movie-feign-hystrix-fallback-factory ;访问:http://localhost:8010/movie/v1/1 ,正常返回结果。
停止服务提供者:micro-provider-user ,再次访问:http://localhost:8010/movie/v1/1 ,返回回退方法里的默认结果。
Hystrix 的监控
除实现容错外,Hystrix 还提供了近乎实时的监控。
HystrixCommand 和 HystrixObservableCommand 在执行时,会生成执行结果和运行指标,比如每秒执行的请求数、成功数等,这些监控数据对分析应用系统的状态很有用。
使用 Hystrix 的模块 hystrix-metrics-event-stream ,就可将这些监控的指标信息以 text/event-stream 的格式暴露给外部系统。spring-cloud-starter-hystrix 中已包含该模块。
为项目添加 spring-boot-starter-actuator ,就可使用 /hystrix.stream 端点获得 Hystrix 的监控信息了。
启动测试。启动 Eureka Server: micro-discovery-eureka 、服务提供者:micro-provider-user 、服务消费者:micro-consumer-movie-ribbon-hystrix ;访问:http://localhost:8010/hystrix.stream ,可看到浏览器一直处于请求的状态,页面空白。这是因为此时项目中注解了
@HystrixCommand的方法还没有被执行,因此也没有任何的监控数据。访问 http://localhost:8010/movie/v1/1 后,再次访问:http://localhost:8010/hystrix.stream ,可看到页面会不断出现监控数据。
这是因为系统会不断地刷新以获得实时的监控数据。 Hystrix 的监控指标非常全面,例如 HystrixCommand 的名称、 group 名称、断路器状态、错误率、错误数等。
Feign 项目的 Hystrix 监控
创建项目。复制项目 micro-consumer-movie-feign-hystrix-fallback ,将 artifactId 修改为 micro-consumer-movie-feign-hystrix-fallback-stream 。
为项目添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在启动类上添加
@EnableCircuitBreaker,这样就可使用 /hystrix.stream 端点了。修改启动端口号,后续聚合多个 /hystrix.stream 端点时用到。
server:
port: 8020
启动测试。启动 Eureka Server: micro-discovery-eureka 、服务提供者:micro-provider-user 、服务消费者:micro-consumer-movie-feign-hystrix-fallback-stream 。
访问 http://localhost:8020/movie/v1/1 后,再次访问:http://localhost:8020/hystrix.stream ,可看到页面会不断出现监控数据。
使用 Hystrix Dashboard 可视化监控数据
访问 /hystrix.stream 端点获得的数据是以文字形式展示的。很难通过这些数据,一眼看出系统当前的运行状态。
可使用 Hystrix Dashboard ,让监控数据图形化、可视化。
下面来编写一个 Hystrix Dashboard 。
使用 Spring Boot 快速构建项目 micro-hystrix-dashboard 。
为项目添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
- 在启动类上添加注解
@EnableHystrixDashboard。
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardApplication.class, args);
}
}
- 设置服务启动端口号。
server:
port: 8030
启动测试。启动 Eureka Server: micro-discovery-eureka 、服务提供者:micro-provider-user 、服务消费者:micro-consumer-movie-ribbon-hystrix 、Hystrix Dashboard :micro-hystrix-dashboard 。
访问 http://localhost:8010/movie/v1/1 后,再次访问:http://localhost:8010/hystrix.stream ,可看到页面会不断出现监控数据。
访问 http://localhost:8030/hystrix ,并输入 http://localhost:8010/hystrix.stream ,点击 “Monitor Stream" 即可看到可视化监控数据。

左上角的圆圈代表了该方法的流量和状态:
- 圆圈越大代表方法流量越大;
- 圆圈为绿色代表断路器健康、黄色代表断路器偶发故障、红色代表断路器故障;
右上角的计数器(三列数字):
第一列从上到下:
- 绿色代表当前成功调用的数量
- 蓝色代表短路请求的数量
- 蓝绿色代表错误请求的数量
第二列从上到下:
- 黄色代表超时请求的数量
- 紫色代表线程池拒绝的数量
- 红色代表失败请求的数量
第三列:
- 过去10s的错误请求百分比
Thread Pools:
- Hystrix 会针对一个受保护的类创建一个对应的线程池,这样做的目的是 Hystrix 的命令被调用的时候,不会受方法请求线程的影响(或者说Hystrix的工作线程和调用者线程相互之间不影响)。
左上角的圆圈代表了该线程池的流量和状态:
- 圆圈越大代表线程池越活跃,流量越大
- 圆圈颜色代表的是线程池的健康状况
左下角从上至下:
- Active 代表线程池中活跃线程的数量
- Queued 代表排队的线程数量,该功能默认禁止,因此默认情况下始终为0
- Pool Size 代表线程池中线程的数量
右下角从上至下:
- Max Active 代表最大活跃线程,这里展示的数据是当前采用周期中,活跃线程的最大值
- Execcutions 代表线程池中线程被调用执行 Hystrix 命令的次数
- Queue Size 代表线程池队列的大小,默认禁用,无意义
Hystrix Dashboard 参数说明见:https://blog.csdn.net/qq_41125219/article/details/121370276
尝试将隔离策略设为 SEMAPHORE ,此时上图中的 ThreadPool 一栏将不再显示。这是由于 THREAD 和 SEMAPHORE 的实现机制不同所导致。
使用 Turbine 聚合监控数据
使用微服务架构的应用系统一般会包含若干个微服务,每个微服务通常都会部署多个实例。如果每次只能查看单个实例的监控数据,就必须在 Hystrix Dashboard 上切换想要监控的地址,这显然很不方便。那要如何解决该问题呢?
Turbine 简介
Turbine 是一个聚合 Hystrix 监控数据的工具,它可将所有相关 Hystrix.stream 端点的数据聚合到一个组合的 turbine.stream 中,从而让集群的监控更加方便。

使用 Spring Boot 快速构建项目 micro-hystrix-turbine 。
为项目添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
- 在启动类上添加注解
@EnableTurbine。
@SpringBootApplication
@EnableTurbine
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
- 修改配置文件
server:
port: 8031
spring:
application:
name: micro-hystrix-turbine
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
turbine:
appConfig: micro-consumer-movie,micro-consumer-movie-feign-hystrix-fallback-stream
clusterNameExpression: "'default'"
Turbine 会在 Eureka Server 中找到 micro-consumer-movie,micro-consumer-movie-feign-hystrix-fallback-stream 这两个微服务,并聚合两个微服务的监控数据。
启动测试。启动 Eureka Server: micro-discovery-eureka 、服务提供者:micro-provider-user 、服务消费者:micro-consumer-movie-ribbon-hystrix 和 micro-consumer-movie-feign-hystrix-fallback-stream、Hystrix Dashboard :micro-hystrix-dashboard 、Turbine :micro-hystrix-turbine。访问 http://localhost:8031/turbine.stream 。
访问 http://localhost:8010/movie/v1/1 和 http://localhost:8020/movie/v1/1 ,再次访问: http://localhost:8031/turbine.strea。
访问 http://localhost:8030/hystrix ,并输入 http://localhost:8031/turbine.stream ,点击 “Monitor Stream" 即可看到可视化监控数据。

使用 Rabbitmq 消息中间件收集监控数据
一些场景下,例如微服务与 Turbine 网络不通,此时,可借助消息中间件实现数据收集各个微服务将 Hystrix Command 的监控数据发送至消息中间件,Turbine 消费消息中间件中的数据。

采用 Docker + Docker-Composer 在虚拟机上快速部署一个 Rabbitmq 。
创建项目。复制 micro-consumer-movie-ribbon-hystrix ,修改 artifactId 为 micro-consumer-movie-ribbon-hystrix-turbine-mq 。
为项目添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- 修改配置文件
server:
port: 8010
spring:
application:
name: micro-consumer-movie
rabbitmq:
host: 192.168.233.131
port: 5672
username: root
password: Pwd
virtual-host: vhost
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
改造 Turbine
创建项目。复制项目 micro-hystrix-turbine ,修改 artifactId 为 micro-hystrix-turbine-mq 。
为项目添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-turbine-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
删除 spring-cloud-starter-turbine 依赖。
- 修改启动类,添加注解
@EnableTurbineStream
@SpringBootApplication
@EnableTurbineStream
public class TurbineMqApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineMqApplication.class, args);
}
}
- 修改配置文件
server:
port: 8031
spring:
application:
name: micro-hystrix-turbine
rabbitmq:
host: 192.168.233.131
port: 5672
username: root
password: Pwd
virtual-host: vhost
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
删除 turbine.appConfig 和 turbine.clusterNameExpression 配置。
启动测试。启动 Eureka Server: micro-discovery-eureka 、服务提供者:micro-provider-user 、服务消费者:micro-consumer-movie-ribbon-hystrix-turbine-mq、Hystrix Dashboard :micro-hystrix-dashboard 、Turbine :micro-hystrix-turbine-mq。访问 http://localhost:8031/turbine.stream 。
访问 http://localhost:8010/movie/v1/1 ,再次访问: http://localhost:8031/turbine.strea。
访问 http://localhost:8030/hystrix ,并输入 http://localhost:8031/turbine.stream ,点击 “Monitor Stream" 即可看到可视化监控数据。
代码仓库
https://gitee.com/chentian114/spring-cloud-practice
公众号
参考
《Spring Cloud 与Docker 微服务架构实战》 周立
边栏推荐
- 基于位置服务(LBS)的SSM的框架实现的兴趣社交软件平台设计与实现
- IPhone 15 forced to use type-C interface
- NGUI,聊天滚动框,UI TextList
- Use bat to write to the first line of the file
- Dotween usage
- 安全相关网站推荐
- Installing mysql5.7 for Linux
- Internet of things security in the era of remote work
- C # introductory series (11) -- multidimensional array
- Windows 安装MySQL5.7 以上的版本(压缩包形式安装)
猜你喜欢

Initial deployment of Servlet

电子设备辐射EMC整改案例

MySQL基础篇常用约束总结上篇

为什么说Web3会是「创作者经济」的游戏规则改变者

Cadence OrCAD capture design method to avoid misoperation graphic tutorial

Dimension types for different CV tasks

安装MySQL ,出现由于找不到 MSVCR120.dll,无法继续执行代码解决方法”

新西兰是道路安全做的最好的国家之一

IPhone 15 forced to use type-C interface

Tree topology networking structure of ZigBee module communication protocol
随机推荐
Wechat applet ordering system with source code
Internet of things security in the era of remote work
Fix the problem that uicollectionview does not reach the bottom security zone
金仓数据库KingbaseES中的sys_checksums坏块检测功能
批量对数据添加噪声并生成新命名标注文件
rpc的正确打开方式|读懂Go原生net/rpc包
Arbitrum 基础架构:快速入门
Série de démarrage C # (XI) - - tableaux multidimensionnels
【DBSCAN】DBSCAN实例
NGUI,选择性别男女
MD5学习
[Bert]: Calculation of last ave state when training tasks with similar Bert semantics
MD5 learning
Batch add noise to data and generate new named annotation files
Ngui, chat scroll box, UI textlist
吴恩达机器学习课程-第七周
NFT 2.0: 下一代的NFT将是精简且值得信赖的NFT
Picture rule page turning
选择DC-DC开关电源控制器的实战过程
详述用网络分析仪测量DC-DC和PDN