Spring Boot 服务容错 Hystrix 入门

1. 概述

在开始 Hystrix 的学习之前,我们先来一起瞅瞅“服务雪崩”、“服务容错”等的概念,方便我们理解为什么要使用 Hystrix 框架。

友情提示:本文是《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》的弟弟篇。所以内容上,会有蛮多重叠的地方。

写本文的主要目的是,分享在纯 Spring Boot 环境下,如何使用 Hystrix 框架。基本所有的网上文章,都是通过 Spring Cloud Netflix Hystrix 进行 Hystrix 的使用,这样就引入了大量 Spring Cloud 的依赖。而我们的希望,可能仅仅只想使用 https://github.com/Netflix/Hystrix 核心库。

不过绝大多数情况下,我们并不会在纯 Spring Boot 环境下,使用 Hystrix 实现服务容错。

1.1 服务雪崩

在微服务的架构体系中,我们会将系统拆分成多个服务小单元,通过 HTTP 或者 RPC 进行远程调用。如下图所示:

服务雪崩 - 正常

在绝大多数情况下,服务消费者都能正常的远程调用服务提供者。但是某一时刻,服务提供者执行逻辑较慢,又或者网络出现抖动的情况,导致服务消费调用服务提供者超时或者失败。如下图所示:

服务雪崩 - 调用失败

如果这个情况持续一段时间,服务提供者的响应一直很慢,导致服务消费者的响应也跟着很慢,最终引起服务消费者的请求任务积压,也跟着一起出问题了。如下图所示:

友情提示:以 SpringMVC 提供 API 接口举例子,因为 Tomcat 的线程池是有限的,如果个别请求处理很慢,会逐步占用到整个线程池,导致后续其它请求无法被处理。

服务雪崩 - 服务雪崩

这种因为一个下游服务的故障,导致上游服务一起跟着故障的现象,我们称为“服务雪崩”。

1.2 服务容错

针对“服务雪崩”的情况,我们需要进行“服务容错”处理。解决的方向很“简单”,尽量不要去调用故障的服务,避免被拖垮。一般常用的手段有,主要是限流开关

① 限流

通过限制调用服务的频率,避免频繁调用故障服务,导致请求任务积压而自身雪崩。

② 开关

通过关闭对故障服务的调用,停止调用故障服务,从而避免服务雪崩。当然,关闭的前提是,不调用故障服务的情况下,业务逻辑依然可以走下去,或者业务数据的完整性不会被破坏。

一般来说,开关会分成手动开关和自动开关。手动开关比较好了解,自动开关是满足指定条件自动进行关闭。

自动开关比较经典的就是“断路器模式”,它源于 Martin Fowler 大佬在 《CircuitBreaker》 文章的分享。

“断路器”,又称自动开关,它是一种既有手动开关作用,又能自动进行失压、欠压、过载、和短路保护的电器

它可用来分配电能,不频繁地启动异步电动机,对电源线路及电动机等实行保护,当它们发生严重的过载或者短路及欠压等故障时能自动切断电路,其功能相当于熔断器式开关与过欠热继电器等的组合。而且在分断故障电流后一般不需要变更零部件,一获得了广泛的应用。

在微服务架构中,“断路器模式”的用途也是类似的。当某个服务提供者发生故障(相当于电器发生短路的情况)时,断路器一旦监控到这个情况,会将开关进行自动关闭。之后,在服务消费者调用该故障服务提供者时,直接抛出错误异常,不进行调用,从而避免调用服务的漫长等待。

友情提示:如果这么描述比较晦涩,稍后我们以 Hystrix 提供的断路器功能,结合它的状态来具体来瞅瞅哈。

1.3 Hystrix

Hystrix 是 Netflix 开源的分布式系统的延迟和容错库。

Hystrix 供分布式系统使用,提供延迟和容错功能,隔离远程系统、访问和第三方程序库的访问点,防止级联失败,保证复杂的分布系统在面临不可避免的失败时,仍能有其弹性。

Hystrix 图标

Netflix 称,在分布式环境中,不可避免会造成一些服务的失败。Hystrix 库旨在控制分布式服务中提供更大容限和服务失败之间的相互关系。Hystrix 通过隔离访问远程系统、服务和第三方库的点,阻止级联故障,从而使复杂的分布式系统更具弹性。

Hystrix 源于 Netflix API 团队在去年启动的弹性工程项目,在此期间,Hystrix 得到了不断发展,并逐渐成熟。现在,在 Netflix 网站中,每天有数十亿的独立线程和信号通过 Hystrix 进行调用,Hystrix 的运行时间和弹性也得到了显著的改善。

Hystrix 比较重要的是三个特性:1)Fallback 服务降级;2)断路器机制;3)资源隔离。

1.3.1 Fallback 服务降级

在服务调用失败时,我们可以通过 Hystrix 实现 Fallback 服务降级。

例如说,对于查询操作,我们给它实现一个 fallback 方法。当请求服务提供者发生异常时,我们可以执行 fallback 方法获得返回结果。示例代码如下:

fallback 示例

一般情况下,fallback 方法的返回结果使用设置的默认值,又或者来自缓存。

友情提示:如果想要深入了解,可阅读《Hystrix 源码解析 —— 请求执行(四)之失败回退逻辑》

1.3.2 断路器机制

Hystrix 内置断路器 HystrixCircuitBreaker 实现,一共有三种状态:

  • CLOSED :关闭
  • OPEN :打开
  • HALF_OPEN :半开

牛逼!HystrixCircuitBreaker 比较神来之笔,就是增加了 HALF_OPEN 状态,不仅仅实现了自动化的关闭,还实现了自动化的打开

其中,断路器处于 OPEN 状态时,链路处于非健康状态,命令执行时,直接调用回退逻辑,跳过正常逻辑。

HystrixCircuitBreaker 状态变迁如下图 :

① 红线 :初始时,断路器处于 CLOSED 状态,链路处于健康状态。当满足如下条件,断路器从 CLOSED 变成 OPEN 状态:

  • 周期( 可配,HystrixCommandProperties.default_metricsRollingStatisticalWindow = 10000 ms )内,总请求数超过一定( 可配,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold = 20 ) 。
  • 错误请求占总请求数超过一定比例( 可配,HystrixCommandProperties.circuitBreakerErrorThresholdPercentage = 50% ) 。

② 绿线 :断路器处于 OPEN 状态,命令执行时,若当前时间超过断路器开启时间一定时间( HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds = 5000 ms ),断路器变成 HALF_OPEN 状态,尝试调用正常逻辑,根据执行是否成功,打开或关闭熔断器【蓝线】。

友情提示:如果想要深入了解,可阅读《Hystrix 源码解析 —— 断路器 HystrixCircuitBreaker》

1.3.3 资源隔离

Hystrix 使用了“舱壁隔离模式”来隔离和限制各个请求,从而实现资源的隔离。

Hystrix 通过线程池信号量(Semaphore) 两种模式来实现隔离。

① 线程池模式

默认情况下,Hystrix 采用线程池模式来实现隔离。

针对调用的每一个服务,我们给其单独分配一个线程池。例如说,产品服务的调用分配在 A 线程池,用户服务的调用分配在 B 线程池。这样隔离后,两个服务的调用不会相互影响。

② 信号量模式

使用线程池模式来隔离时,需要进行上下文的切换,带来一定的性能损耗。因此,如果对性能有较高要求,且能够接受信号量模式不支持超时的情况,可以考虑采用信号量模式。

友情提示:如果想要深入了解,可阅读《Hystrix 源码解析 —— 命令执行(二)之执行隔离策略》


下面,我们正式开始本文的旅程,开始学习 Hystrix 咯。

2. 快速入门

示例代码对应仓库:

  • 用户服务:labx-23-user-service
  • Hystrix 示例项目:lab-57-hystrix-demo01

本小节,我们来搭建一个 Hystrix 组件的快速入门示例。步骤如下:

  • 首先,搭建一个 user-service 用户服务,提供获取用户信息的 HTTP API 接口。
  • 然后,搭建一个用户服务的消费者,使用 Hystrix 实现服务容错。

2.1 搭建用户服务

创建 labx-23-user-service 项目,搭建用户服务。代码如下图所示:

比较简单,主要是提供 http://127.0.0.1:18080/user/get 接口,获取用户详情。具体的代码,肯定不用艿艿啰嗦讲解哈,点击 labx-23-user-service 查看。

2.2 搭建 Hystrix 示例项目

创建 lab-57-hystrix-demo01 项目,搭建一个用户服务的消费者,使用 Hystrix 实现服务容错。代码如下图所示:

2.2.1 引入依赖

在 pom.xml 文件中,引入 Hystrix 相关依赖。



    
        lab-57
        cn.iocoder.springboot.labs
        1.0-SNAPSHOT
    
    4.0.0

    lab-57-hystrix-demo01

    
        1.8
        1.8
        2.2.4.RELEASE
    

    
        
            
                org.springframework.boot
                spring-boot-starter-parent
                ${spring.boot.version}
                pom
                import
            
        
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            com.netflix.hystrix
            hystrix-core
            1.5.18
        
        
            com.netflix.hystrix
            hystrix-javanica
            1.5.18
        

    

① 引入 hystrix-core 依赖,提供 Hystrix 核心实现。

② 引入 hystrix-javanica 依赖,提供 @HystrixCommand 等注解,搭配 AOP 切面功能,简化我们使用 Hystrix 实现服务容错的功能。

2.2.2 HystrixConfig

创建 HystrixConfig 配置类,创建 HystrixCommandAspect Bean。代码如下:

@Configuration
@EnableAspectJAutoProxy // 开启 AOP 代理的支持
public class HystrixConfig {

    @Bean
    public HystrixCommandAspect hystrixCommandAspect() {
        return new HystrixCommandAspect();
    }

}

通过 HystrixCommandAspect 可以进行扫描 @@HystrixCommand 等注解的切面,使用 Hystrix 进行服务容错。核心代码如下图:

Spring Boot 服务容错 Hystrix 入门_第1张图片

2.2.3 DemoController

创建 DemoController 类,提供调用用户服务的 HTTP API 接口。代码如下:

@RestController
@RequestMapping("/demo")
public class DemoController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/get_user")
    @HystrixCommand(fallbackMethod = "getUserFallback")
    public String getUser(@RequestParam("id") Integer id) {
        logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id);
        return restTemplate.getForEntity("http://127.0.0.1:18080/user/get?id=" + id, String.class).getBody();
    }

    public String getUserFallback(Integer id, Throwable throwable) {
        logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable));
        return "mock:User:" + id;
    }

}

① 在 #getUser(Integer id) 方法,我们使用 RestTemplate 调用用户服务提供的 /user/get 接口,获取用户详情。

② 在 #getUser(Integer id) 方法,添加了 Hystrix 提供的 @HystrixCommand 注解,设置执行发生 Exception 异常时,执行 fallbackMethod 属性对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常

通过不同的 Throwable 异常,我们可以进行不同的 fallback 降级处理。极端情况下,Hystrix 断路器熔断(打开)时,不会执行 #getUser(Integer id) 方法,而是直接抛出 Hystrix circuit short-circuited and is OPEN 异常,然后也是进入 fallback 降级处理。

③ 我们来完整看看 @HystrixCommand 注解的参数:

1. fallbackMethod 属性:

指定 fallback 服务降级的处理方法,处理相应的异常。

2. ignoreExceptions 属性:

指定忽略指定的异常 Class,不进行 fallback 服务降级。

3. commandKey 属性:

Hystrix Command 命令,默认未配置情况下,使用方法名。例如说,我们上面的 #getUser(...) 对应的 commandKey 属性默认为 getUser

4. groupKey 属性:

Hystrix Command 命令分组键,用于 Hystrix 根据不同的分组来统计命令的统计、告警、仪表盘信息。

默认未配置情况下,使用方法所在类名。例如说,我们上面的 #getUser(...) 方法所在类为 DemoController,则对应的 groupKey 属性默认为 DemoController。

5. threadPoolKey 属性

线程池名,用于划分不同的线程池,进行资源隔离。

默认未配置情况下,相同 groupKey 的 Hystrix Command 使用同一个线程池。在配置情况下,相同 groupKey + threadPoolKey 使用同一个线程池。也就是说,groupKey 是必选基础维度,而 threadPoolKey 是可选的进一步细化维度。

2.2.4 DemoApplication

创建 DemoApplication 类,作为 Hystrix 示例项目的启动类。代码如下:

@SpringBootApplication
public class DemoApplication {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

2.3 简单测试

执行 UserServiceApplication 启动用户服务,执行 DemoApplication 启动 Hystrix 示例项目。

① 使用浏览器,访问 http://127.0.0.1:8080/demo/get_user?id=1 地址,成功调用用户服务,返回结果为 User:1

② 停止 UserServiceApplication 关闭用户服务。

使用浏览器,访问 http://127.0.0.1:8080/demo/get_user?id=1 地址,失败调用用户服务,返回结果为 mock:User:1

此时我们会看到如下日志,可以判断触发 Hystrix 的 fallback 服务降级的方法。

2020-05-09 22:32:07.729  INFO 68093 --- [emoController-6] c.i.s.l.h.controller.DemoController      : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-09 22:32:07.731  INFO 68093 --- [emoController-6] c.i.s.l.h.controller.DemoController      : [getUserFallback][id(1) exception(ConnectException: Connection refused (Connection refused))

③ 疯狂使用浏览器,访问 http://127.0.0.1:8080/demo/get_user?id=1 地址,会触发 Hystrix 断路器熔断(打开),不再执行 #getUser(Integer id) 方法,而是直接 fallback 触发 #getUserFallback(Integer id, Throwable throwable) 方法。日志内容如下:

2020-05-09 22:34:20.946  INFO 68093 --- [nio-8080-exec-2] c.i.s.l.h.controller.DemoController      : [getUserFallback][id(1) exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2020-05-09 22:34:21.099  INFO 68093 --- [nio-8080-exec-3] c.i.s.l.h.controller.DemoController      : [getUserFallback][id(1) exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2020-05-09 22:34:21.173  INFO 68093 --- [nio-8080-exec-4] c.i.s.l.h.controller.DemoController      : [getUserFallback][id(1) exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]

④ 重新执行 UserServiceApplication 启动用户服务。

使用浏览器,多次访问 http://127.0.0.1:8080/demo/get_user?id=1 地址,Hystrix 断路器的状态逐步从打开 => 半开 => 关闭。日志内容如下:

// 打开
2020-05-09 22:39:05.226  INFO 68093 --- [io-8080-exec-10] c.i.s.l.h.controller.DemoController      : [getUserFallback][id(1) exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
...

// 半开
[getUser][准备调用 user-service 获取用户(1)详情]

// 关闭
[getUser][准备调用 user-service 获取用户(1)详情]
[getUser][准备调用 user-service 获取用户(1)详情]
[getUser][准备调用 user-service 获取用户(1)详情]
...

3. 请求缓存

和《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》的「3. 请求缓存」使用方式一模一样,胖友点击查看即可。

4. 请求合并

和《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》的「4. 请求合并」使用方式一模一样,胖友点击查看即可。

5. Hystrix Dashboard 监控

hystrix-dashboard 已经废弃,官方描述如下:

This project previously was a part of the Netflix/Hystrix project. It is now deprecated and no longer supported. See the below security section for necessary security considerations.

Spring Cloud Netflix 单独维护了 spring-cloud-netflix-hystrix-dashboard 项目,作为在 Spring Cloud 架构下的 Hystrix Dashboard 监控仪表盘。

因此,如果胖友真的要使用的话,可能只能暂时阅读《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》的「5. Hystrix Dashboard 监控」和「6. Turbine 聚合监控」小节。

6. 集成到 Dubbo

示例代码对应仓库:

  • 用户服务 API:lab-57-hystrix-dubbo-demo-user-service-api
  • 用户服务 Provider:lab-57-hystrix-dubbo-demo-user-service
  • Hystrix 示例项目:lab-57-hystrix-dubbo-demo-application

本小节我们来进行 Dubbo 和 Hystrix 的整合,目前暂时没有框架或者库提供它们的整合,所以我们只能像「2. 快速入门」小节,使用 @HystrixCommand 注解添加在进行 Dubbo RPC 调用的方法上,如下图所示:

Dubbo 示例 + `@HystrixCommand` 注解

Apache Dubbo™ 是一款高性能Java RPC框架。

下面,我们来搭建 Dubbo 和 Hystrix 整合的示例,最终项目如下图所示:

Spring Boot 服务容错 Hystrix 入门_第2张图片

6.1 用户服务 API

创建 lab-57-hystrix-dubbo-demo-user-service-api 项目,作为用户服务 API。如下图所示:

Spring Boot 服务容错 Hystrix 入门_第3张图片

6.1.1 引入依赖

创建 pom.xml 文件,无需引入依赖。



    
        lab-57-hystrix-dubbo-demo
        cn.iocoder.springboot.labs
        1.0-SNAPSHOT
    
    4.0.0

    lab-57-hystrix-dubbo-demo-user-service-api

6.1.2 UserService

创建 UserService 接口,提供用户服务 API 接口。代码如下:

public interface UserService {

    String getUser(Integer id);

}

6.2 用户服务 Provider

创建 lab-57-hystrix-dubbo-demo-user-service 项目,作为用户服务 Provider 实现。如下图所示:

Spring Boot 服务容错 Hystrix 入门_第4张图片

友情提示:对 Dubbo 不了解的胖友可以看看《芋道 Spring Boot Dubbo 入门》文章。

考虑到 Nacos 作为注册中心越来越流行,所以这里就使用 Nacos 啦,不了解的胖友可以看看《芋道 Spring Boot 注册中心 Nacos 入门》文章。

6.2.1 引入依赖

创建 pom.xml 文件,引入 Dubbo、Nacos 依赖。



    
        lab-57-hystrix-dubbo-demo
        cn.iocoder.springboot.labs
        1.0-SNAPSHOT
    
    4.0.0

    lab-57-hystrix-dubbo-demo-user-service

    
        1.8
        1.8
        2.2.4.RELEASE
    

    
        
            
                org.springframework.boot
                spring-boot-starter-parent
                ${spring.boot.version}
                pom
                import
            
        
    

    
        
        
            cn.iocoder.springboot.labs
            lab-57-hystrix-dubbo-demo-user-service-api
            1.0-SNAPSHOT
        

        
        
            org.springframework.boot
            spring-boot-starter
        

        
        
            org.apache.dubbo
            dubbo
            2.7.4.1
        
        
            org.apache.dubbo
            dubbo-spring-boot-starter
            2.7.4.1
        

        
        
            com.alibaba.nacos
            nacos-client
            1.2.1
        
        
            org.apache.dubbo
            dubbo-registry-nacos
            2.7.4.1
        
    

6.2.2 配置文件

创建 application.yaml 配置文件,添加 Dubbo 配置项,并使用 Nacos 作为注册中心。

# dubbo 配置项,对应 DubboConfigurationProperties 配置类
dubbo:
  # Dubbo 应用配置
  application:
    name: user-service # 应用名
  # Dubbo 注册中心配
  registry:
    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。
  # Dubbo 服务提供者协议配置
  protocol:
    port: -1 # 协议端口。使用 -1 表示随机端口。
    name: dubbo # 使用 `dubbo://` 协议。更多协议,可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档
  # Dubbo 服务提供者配置
  provider:
    timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改
    UserRpcService:
      version: 1.0.0
  # 配置扫描 Dubbo 自定义的 @Service 注解,暴露成 Dubbo 服务提供者
  scan:
    base-packages: cn.iocoder.springboot.lab57.userservice.service

① dubbo 配置项,添加 Dubbo 相关的配置项。

② dubbo.registry.address 配置项,设置使用 Nacos 作为配置中心。

6.2.3 UserServiceImpl

创建 UserServiceImpl 类,实现 UserService 接口,提供 Dubbo 用户服务。代码如下:

@org.apache.dubbo.config.annotation.Service(protocol = "dubbo", version = "1.0.0")
public class UserServiceImpl implements UserService {

    @Override
    public String getUser(Integer id) {
        return "User:" + id;
    }

}

6.2.4 UserServiceApplication

创建 UserServiceApplication 类,用户服务的启动类。代码如下:

@SpringBootApplication
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class);
    }

}

6.3 搭建 Hystrix 示例项目

创建 lab-57-hystrix-dubbo-demo-application 项目,搭建一个用户服务的消费者,使用 Dubbo 进行调用,使用 Hystrix 实现服务容错。如下图所示:

6.3.1 引入依赖

创建 pom.xml 文件,引入 Hystrix、Dubbo、Nacos 依赖。



    
        lab-57-hystrix-dubbo-demo
        cn.iocoder.springboot.labs
        1.0-SNAPSHOT
    
    4.0.0

    lab-57-hystrix-dubbo-demo-application

    
        1.8
        1.8
        2.2.4.RELEASE
    

    
        
            
                org.springframework.boot
                spring-boot-starter-parent
                ${spring.boot.version}
                pom
                import
            
        
    

    
        
        
            cn.iocoder.springboot.labs
            lab-57-hystrix-dubbo-demo-user-service-api
            1.0-SNAPSHOT
        

        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.apache.dubbo
            dubbo
            2.7.4.1
        
        
            org.apache.dubbo
            dubbo-spring-boot-starter
            2.7.4.1
        

        
        
            com.alibaba.nacos
            nacos-client
            1.2.1
        
        
            org.apache.dubbo
            dubbo-registry-nacos
            2.7.4.1
        

        
        
            com.netflix.hystrix
            hystrix-core
            1.5.18
        
        
            com.netflix.hystrix
            hystrix-javanica
            1.5.18
        
    

相比「6.2.1 引入依赖」小节来说,多引入 Hystrix 相关依赖进行服务容错。

6.3.2 配置文件

创建 application.yaml 配置文件,添加 Dubbo 和 Nacos 配置项。

# dubbo 配置项,对应 DubboConfigurationProperties 配置类
dubbo:
  # Dubbo 应用配置
  application:
    name: demo-consumer # 应用名
  # Dubbo 注册中心配置
  registry:
    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心,可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。
  # Dubbo 消费者配置
  consumer:
    timeout: 1000 # 【重要】远程服务调用超时时间,单位:毫秒。默认为 1000 毫秒,胖友可以根据自己业务修改
    UserRpcService:
      version: 1.0.0

和「6.2.2 配置文件」小节差不多,不重复啰嗦哈。

6.3.3 HystrixConfig

HystrixConfig

创建 HystrixConfig Bean。代码如下:

@Configuration
@EnableAspectJAutoProxy // 开启 AOP 代理的支持
public class HystrixConfig {

    @Bean
    public HystrixCommandAspect hystrixCommandAspect() {
        return new HystrixCommandAspect();
    }

}

和「2.2.2 HystrixConfig」小节一样。

6.3.4 DemoController

创建 DemoController 类,提供一个通过 Dubbo 调用用户服务提供者的 HTTP 接口。代码如下:

@RestController
@RequestMapping("/demo")
public class DemoController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Reference(protocol = "dubbo", version = "1.0.0")
    private UserService userService;

    @GetMapping("/get_user")
    @HystrixCommand(fallbackMethod = "getUserFallback")
    public String getUser(@RequestParam("id") Integer id) {
        logger.info("[getUser][准备调用 user-service 获取用户({})详情]", id);
        return userService.getUser(id);
    }

    public String getUserFallback(Integer id, Throwable throwable) {
        logger.info("[getUserFallback][id({}) exception({})]", id, ExceptionUtils.getRootCauseMessage(throwable));
        return "mock:User:" + id;
    }

}

在 #getUser(Integer id) 方法上,添加了 @HystrixCommand 注解,从而在 Dubbo 调用失败时,执行 #getUserFallback(Integer id, Throwable throwable) 方法进行服务容错。

6.3.5 DemoApplication

创建 DemoApplication 类,创建应用启动类。代码如下:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

6.4 简单测试

执行 UserServiceApplication 启动用户服务,执行 DemoApplication 启动 Hystrix 示例项目。

① 使用浏览器,访问 http://127.0.0.1:8080/feign-demo/get_user?id=1 地址,成功调用用户服务,返回结果为 User:1

② 停止 UserServiceApplication 关闭用户服务。

使用浏览器,访问 http://127.0.0.1:8080/feign-demo/get_user?id=1 地址,失败调用用户服务,返回结果为 mock:User:1

此时我们会看到如下日志,可以判断触发 Hystrix 的 fallback 服务降级的方法。

2020-05-15 07:18:06.183  INFO 75980 --- [emoController-3] c.i.s.l.demo.controller.DemoController   : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-15 07:18:06.184  INFO 75980 --- [emoController-3] c.i.s.l.demo.controller.DemoController   : [getUserFallback][id(1) exception(RemotingException: message can not send, because channel is closed . url:dubbo://192.168.43.240:20880/com.alibaba.cloud.dubbo.service.DubboMetadataService?anyhost=true&application=demo-consumer&bind.ip=192.168.43.240&bind.port=20880&check=false&codec=dubbo&deprecated=false&dubbo=2.0.2&dynamic=true&generic=true&group=user-service&heartbeat=60000&interface=com.alibaba.cloud.dubbo.service.DubboMetadataService&lazy=false&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedURLs&pid=75980&qos.enable=false®ister.ip=192.168.43.240&release=2.7.4.1&remote.application=user-service&revision=2.2.0.RELEASE&side=consumer&sticky=false×tamp=1589498209533&version=1.0.0)]

③ 疯狂使用浏览器,访问 http://127.0.0.1:8080/feign-demo/get_user?id=1 地址,会触发 Hystrix 断路器熔断(打开),不再执行 #getUser(Integer id) 方法,而是直接 fallback 触发 #getUserFallback(Integer id, Throwable throwable) 方法。日志内容如下: 

2020-05-15 07:20:02.809  INFO 75980 --- [nio-8080-exec-1] c.i.s.l.demo.controller.DemoController   : [getUserFallback][id(1) exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2020-05-15 07:20:02.907  INFO 75980 --- [nio-8080-exec-2] c.i.s.l.demo.controller.DemoController   : [getUserFallback][id(1) exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
2020-05-15 07:20:02.995  INFO 75980 --- [nio-8080-exec-3] c.i.s.l.demo.controller.DemoController   : [getUserFallback][id(1) exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]

④ 重新执行 UserServiceApplication 启动用户服务。

使用浏览器,多次访问 http://127.0.0.1:8080/feign-demo/get_user?id=1 地址,Hystrix 断路器的状态逐步从打开 => 半开 => 关闭。日志内容如下:

// 打开
2020-05-14 21:27:28.560  INFO 71995 --- [nio-8080-exec-5] .f.UserServiceFeignClientFallbackFactory : [getUserFallback][id(1) exception(RuntimeException: Hystrix circuit short-circuited and is OPEN)]
...

// 半开
2020-05-15 07:21:43.486  INFO 75980 --- [moController-10] c.i.s.l.demo.controller.DemoController   : [getUser][准备调用 user-service 获取用户(1)详情]

// 关闭
2020-05-15 07:21:50.449  INFO 75980 --- [moController-10] c.i.s.l.demo.controller.DemoController   : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-15 07:21:50.653  INFO 75980 --- [moController-10] c.i.s.l.demo.controller.DemoController   : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-15 07:21:50.818  INFO 75980 --- [moController-10] c.i.s.l.demo.controller.DemoController   : [getUser][准备调用 user-service 获取用户(1)详情]
...

 

你可能感兴趣的:(Hystrix,Spring,Boot,服务容错)