《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Spring-Boot/Resilience4j/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labslab-59 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

友情提示:如下关于 Resilience4j 的介绍,英文对应 https://resilience4j.readme.io/docs/getting-started 文档。

Resilience4j 是一个轻量级容错组件,其灵感来自于 Hystrix,但主要为 Java 8 和函数式编程所设计。

轻量级体现在其只用 Vavr 库,没有任何外部依赖。而 Hystrix 依赖了 Archaius,Archaius 本身又依赖很多第三方包,例如 Guava、Apache Commons Configuration 等等。

友情提示:如果胖友对服务容错、服务降级、服务雪崩、熔断器等概念不是很熟悉,建议先看看《芋道 Spring Boot 服务容错 Hystrix 入门》文章的「1. 概述」小节。

一来是这些概念面试容易问,二来整清楚为什么要使用容错组件。

要使用 Resilience4j,不需要引入所有依赖,只需要引入你所需的模块。

Resilience4j 项目目录

友情提示:模块比较丰富,重要的艿艿已经加粗

  • Core modules 核心模块

    • resilience4j-circuitbreaker: Circuit breaking 熔断器
    • resilience4j-ratelimiter: Rate limiting 限流
    • resilience4j-bulkhead: Bulkheading 舱壁隔离
    • resilience4j-retry: Automatic retrying (sync and async) 自动重试(同步和异步)
    • resilience4j-cache: Result caching 响应缓存

      看了下官方文档,Resilience4j 提供的 Cache 功能,指的是针对 JSR 107 JCACHE 的集成,和 Hystrix 的请求缓存不是一个东东。

      ISSUE#815 的讨论,也可以看出,就是针对 JCACHE 的容错。

    • resilience4j-timelimiter: Timeout handling 超时处理

  • Add-on modules
    • resilience4j-retrofit: Retrofit adapter
    • resilience4j-feign: Feign adapter
    • resilience4j-consumer: Circular Buffer Event consumer
    • resilience4j-kotlin: Kotlin coroutines support
  • Frameworks modules
    • resilience4j-spring-boot: Spring Boot Starter
    • resilience4j-spring-boot2: Spring Boot 2 Starter
    • resilience4j-ratpack: Ratpack Starter
    • resilience4j-vertx: Vertx Future decorator
  • Reactive modules
    • resilience4j-rxjava2: Custom RxJava2 operators
    • resilience4j-reactor: Custom Spring Reactor operators
  • Metrics modules
    • resilience4j-micrometer: Micrometer Metrics exporter
    • resilience4j-metrics: Dropwizard Metrics exporter
    • resilience4j-prometheus: Prometheus Metrics exporter

下面,我们开始愉快的入门吧~

2. CircuitBreaker 熔断器

示例代码对应仓库:

CircuitBreaker 一共有 CLOSEDOPENHALF_OPEN 三种状态,通过状态机实现。转换关系如下图所示:

Circuitbreaker 状态机

  • 当熔断器关闭(CLOSED)时,所有的请求都会通过熔断器。
  • 如果失败率超过设定的阈值,熔断器就会从关闭状态转换到打开(OPEN)状态,这时所有的请求都会被拒绝。
  • 当经过一段时间后,熔断器会从打开状态转换到半开(HALF_OPEN)状态,这时仅有一定数量的请求会被放入,并重新计算失败率。如果失败率超过阈值,则变为打开(OPEN)状态;如果失败率低于阈值,则变为关闭(CLOSE)状态。

友情提示:下面会涉及到 CircuitBreaker 如何实现,可以稍微看看,简单理解。想要深入的胖友,可以阅读《Resilience4j 源码解析》中 CircuitBreaker 部分。

Resilience4j 记录请求状态的数据结构和 Hystrix 不同:Hystrix 是使用滑动窗口来进行存储的,而 Resilience4j 采用的是 Ring Bit Buffer(环形缓冲区)。Ring Bit Buffer 在内部使用 BitSet 这样的数据结构来进行存储,结构如下图所示:

环形缓冲区

  • 每一次请求的成功或失败状态只占用一个 bit 位,与 boolean 数组相比更节省内存。BitSet 使用 long[] 数组来存储这些数据,意味着 16 个值(64 bit)的数组可以存储 1024 个调用状态。

计算失败率需要填满环形缓冲区。例如,如果环形缓冲区的大小为 10,则必须至少请求满 10 次,才会进行故障率的计算。如果仅仅请求了 9 次,即使 9 个请求都失败,熔断器也不会打开。但是 CLOSE 状态下的缓冲区大小设置为 10 并不意味着只会进入 10 个请求,在熔断器打开之前的所有请求都会被放入。

  • 当故障率高于设定的阈值时,熔断器状态会从由 CLOSE 变为 OPEN。这时所有的请求都会抛出 CallNotPermittedException 异常。
  • 当经过一段时间后,熔断器的状态会从 OPEN 变为 HALF_OPENHALF_OPEN 状态下同样会有一个 Ring Bit Buffer,用来计算HALF_OPEN 状态下的故障率。如果高于配置的阈值,会转换为 OPEN,低于阈值则装换为 CLOSE。与 CLOSE 状态下的缓冲区不同的地方在于,HALF_OPEN 状态下的缓冲区大小会限制请求数,只有缓冲区大小的请求数会被放入。

除此以外,熔断器还会有两种特殊状态:DISABLED(始终允许访问)和 FORCED_OPEN(始终拒绝访问)。这两个状态不会生成熔断器事件(除状态装换外),并且不会记录事件的成功或者失败。退出这两个状态的唯一方法是触发状态转换或者重置熔断器。

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

  • 首先,使用 SpringMVC 搭建一个用户服务,提供 JSON 数据格式的 HTTP API。
  • 然后,搭建一个使用 Feign 声明式调用用户服务 HTTP API 的示例项目。

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

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

2.1 搭建用户服务

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

项目代码

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

2.2 搭建 Resilience4j 示例项目

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

项目代码

2.2.1 引入依赖

创建 pom.xml 文件,引入 Resilience4j 相关依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lab-59</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>lab-59-resilience4j-demo01</artifactId>

<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入 Resilience4j Starter 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.4.0</version>
</dependency>

<!-- 引入 Aspectj 依赖,支持 AOP 相关注解、表达式等等 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>

</dependencies>

</project>

① 添加 resilience4j-spring-boot2 依赖,引入 Resilience4j Starter 相关依赖,并实现对其的自动配置。

② 添加 aspectjrtaspectjweaver 依赖,引入 Aspectj 依赖,支持 AOP 相关注解、表达式等等。因为 Resilience4j 实现其功能,可以通过注解 + AOP的组合,而 AOP 的功能是基于 Aspectj 工具库来实现的。

2.2.2 配置文件

创建 application.yml 配置文件,添加 Resilience4j CircuitBreaker 相关配置项。

resilience4j:
# Resilience4j 的熔断器配置项,对应 CircuitBreakerProperties 属性类
circuitbreaker:
instances:
backendA:
failure-rate-threshold: 50 # 熔断器关闭状态和半开状态使用的同一个失败率阈值,单位:百分比。默认为 50
ring-buffer-size-in-closed-state: 5 # 熔断器关闭状态的缓冲区大小,不会限制线程的并发量,在熔断器发生状态转换前所有请求都会调用后端服务。默认为 100
ring-buffer-size-in-half-open-state: 5 # 熔断器半开状态的缓冲区大小,会限制线程的并发量。例如,缓冲区为 10 则每次只会允许 10 个请求调用后端服务。默认为 10
wait-duration-in-open-state : 5000 # 熔断器从打开状态转变为半开状态等待的时间,单位:微秒
automatic-transition-from-open-to-half-open-enabled: true # 如果置为 true,当等待时间结束会自动由打开变为半开;若置为 false,则需要一个请求进入来触发熔断器状态转换。默认为 true
register-health-indicator: true # 是否注册到健康监测

resilience4j.circuitbreaker 是 Resilience4j 的熔断器配置项,对应 CircuitBreakerProperties 属性类。

② 在 resilience4j.circuitbreaker.instances 配置项下,可以添加熔断器实例的配置,其中 key 为熔断器实例的名字value 为熔断器实例的具体配置,对应 CircuitBreakerConfigurationProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendA" 的熔断器,具体的每个项,胖友看看艿艿添加在后面的注释哈。

③ 在 resilience4j.circuitbreaker.configs 配置项下,可以添加通用配置项,提供给 resilience4j.circuitbreaker.instances 熔断器使用。示例如下图:

`resilience4j.circuitbreaker.configs` 示例

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")
@CircuitBreaker(name = "backendA", 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, throwable.getClass().getSimpleName());
return "mock:User:" + id;
}

}

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

② 在 #getUser(Integer id) 方法,添加了 Resilience4j 提供的 @CircuitBreaker 注解:

  • 通过 name 属性,设置对应的 CircuitBreaker 熔断器实例名为 "backendA",就是我们在「2.2.2 配置文件」中所添加的。
  • 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常

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

2.2.4 DemoApplication

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

@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 启动 Resilience4j 示例项目。

① 使用浏览器,访问 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

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

2020-05-19 08:46:44.094  INFO 65728 --- [nio-8080-exec-1] c.i.s.l.r.controller.DemoController      : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-19 08:46:44.119 INFO 65728 --- [nio-8080-exec-1] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(ResourceAccessException)]

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

2020-05-19 08:47:03.107  INFO 65728 --- [nio-8080-exec-7] c.i.s.l.r.controller.DemoController      : [getUserFallback][id(1) exception(CallNotPermittedException)]
2020-05-19 08:47:03.204 INFO 65728 --- [nio-8080-exec-8] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(CallNotPermittedException)]
2020-05-19 08:47:03.275 INFO 65728 --- [nio-8080-exec-9] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(CallNotPermittedException)]

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

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

// 打开
2020-05-19 08:47:39.408 INFO 65728 --- [nio-8080-exec-1] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(CallNotPermittedException)]
...

// 半开
2020-05-19 08:48:23.095 INFO 65728 --- [nio-8080-exec-5] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]

// 关闭
2020-05-19 08:48:32.488 INFO 65728 --- [nio-8080-exec-6] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-19 08:48:32.666 INFO 65728 --- [nio-8080-exec-7] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-19 08:48:33.230 INFO 65728 --- [nio-8080-exec-9] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]
...

3. RateLimiter 限流器

示例代码对应仓库:

RateLimiter 一共有两种实现类:

默认情况下,采用 AtomicRateLimiter 基于令牌桶限流算法实现限流。= = 搜了一圈 Resilience4j 的源码,貌似 SemaphoreBasedRateLimiter 没有地方在使用,难道已经被抛弃了~

FROM 《接口限流实践》

令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

下面,我们直接在「2. 快速入门」小节的基础上,直接增加 Resilience4j 限流器相关的示例代码。改动点如下图所示:

项目调整

3.1 配置文件

修改 application.yml 配置文件,添加 Resilience4j RateLimiter 相关配置项。

resilience4j:
# Resilience4j 的限流器配置项,对应 RateLimiterProperties 属性类
ratelimiter:
instances:
backendB:
limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50
limit-refresh-period: 10s # 每个周期的时长,单位:微秒。默认为 500
timeout-duration: 5s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s
register-health-indicator: true # 是否注册到健康监测

resilience4j.ratelimiter 是 Resilience4j 的 RateLimiter 配置项,对应 RateLimiterConfigurationProperties 属性类。

② 在 resilience4j.ratelimiter.instances 配置项下,可以添加限流器实例的配置,其中 key 为限流器实例的名字value 为限流器实例的具体配置,对应 RateLimiterConfigurationProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendB" 的限流器,具体的每个项,胖友看看艿艿添加在后面的注释哈。有一点要注意,在请求被限流时,并不是直接失败抛出异常,而是阻塞等待最大 timeout-duration 微秒,看看是否能够请求通过

③ 在 resilience4j.ratelimiter.configs 配置项下,可以添加通用配置项,提供给 resilience4j.ratelimiter.instances 限流器使用。示例如下图:

`resilience4j.ratelimiter.configs` 示例

3.2 RateLimiterDemoController

创建 RateLimiterDemoController 类,提供示例 HTTP API 接口。代码如下:

@RestController
@RequestMapping("/rate-limiter-demo")
public class RateLimiterDemoController {

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

@GetMapping("/get_user")
@RateLimiter(name = "backendB", fallbackMethod = "getUserFallback")
public String getUser(@RequestParam("id") Integer id) {
return "User:" + id;
}

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

}

① 在 #getUser(Integer id) 方法,我们直接返回 "User:{id}",不进行任何逻辑。

② 在 #getUser(Integer id) 方法,添加了 Resilience4j 提供的 @RateLimiter 注解:

  • 通过 name 属性,设置对应的 RateLimiter 实例名为 "backendB",就是我们在「3.1 配置文件」中所添加的。
  • 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常

在请求被限流时,Resilience4j 不会执行 #getUser(Integer id) 方法,而是直接抛出 RequestNotPermitted 异常,然后就进入 fallback 降级处理。

友情提示:注意,@RateLimiter 注解的 fallbackMethod 属性对应的 fallback 方法,不仅仅处理被限流时抛出的 RequestNotPermitted 异常,也处理 #getUser(Integer id) 方法执行时的普通异常。

3.3 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

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

立马使用浏览器再次访问,会阻塞等待 < 5 秒左右,降级返回 mock:User:1。同时,我们在 IDEA 控制台的日志中,可以看到被限流时抛出的 RequestNotPermitted 异常。

2020-05-19 21:50:42.585  INFO 79815 --- [nio-8080-exec-1] c.i.s.l.r.c.RateLimiterDemoController    : [getUserFallback][id(1) exception(RequestNotPermitted)]

另外,我们将 @RateLimiter@CircuitBreaker 注解添加在相同方法上,进行组合使用,来实现限流和断路的作用。但是要注意,需要添加 resilience4j.circuitbreaker.instances.<instance_name>.ignoreExceptions=io.github.resilience4j.ratelimiter.RequestNotPermitted 配置项,忽略限流抛出的 RequestNotPermitted 异常,避免触发断路器的熔断。

4. Bulkhead 舱壁

示例代码对应仓库:

Bulkhead 指的是船舶中的舱壁,它将船体分隔为多个船舱,在船部分受损时可避免沉船。

在 Resilience4j 中,提供了基于 Semaphore 信号量ThreadPool 线程池两种 Bulkhead 实现,隔离不同种类的调用,并提供流控的能力,从而避免某类调用异常时而占用所有资源,导致影响整个系统。

下面,我们直接在「2. 快速入门」小节的基础上,直接增加 Resilience4j 舱壁相关的示例代码。改动点如下图所示:

项目调整

良心的艿艿,两种类型的 Bulkhead 使用示例都提供了噢。

友情提示:我们先来看信号量类型的 Bulkhead 示例。
友情提示:我们先来看信号量类型的 Bulkhead 示例。
友情提示:我们先来看信号量类型的 Bulkhead 示例。

4.1 配置文件

修改 application.yml 配置文件,添加 Resilience4j 信号量类型的 Bulkhead 相关配置项。

resilience4j:
# Resilience4j 的信号量 Bulkhead 配置项,对应 BulkheadConfigurationProperties 属性类
bulkhead:
instances:
backendC:
max-concurrent-calls: 1 # 并发调用数。默认为 25
max-wait-duration: 5s # 并发调用到达上限时,阻塞等待的时长,单位:微秒。默认为 0

resilience4j.bulkhead 是 Resilience4j 的信号量 Bulkhead 配置项,对应 BulkheadProperties 属性类。

② 在 resilience4j.bulkhead.instances 配置项下,可以添加 Bulkhead 实例的配置,其中 key 为 Bulkhead 实例的名字value 为 Bulkhead 实例的具体配置,对应 BulkheadProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendC" 的 Bulkhead,具体的每个项,胖友看看艿艿添加在后面的注释哈。有一点要注意,在请求被流控时,并不是直接失败抛出异常,而是阻塞等待最大 max-wait-duration 微秒,看看是否能够请求通过

友情提示:这里我们设置 max-concurrent-calls 配置项为 1,仅允许并发调用数为 1,方便测试。

③ 在 resilience4j.bulkhead.configs 配置项下,可以添加通用配置项,提供给 resilience4j.bulkhead.instances Bulkhead 使用。示例如下图:

`resilience4j.bulkhead.configs` 示例

4.2 BulkheadDemoController

创建 BulkheadDemoController 类,提供示例 HTTP API 接口。代码如下:

@RestController
@RequestMapping("/bulkhead-demo")
public class BulkheadDemoController {

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

@GetMapping("/get_user")
@Bulkhead(name = "backendC", fallbackMethod = "getUserFallback", type = Bulkhead.Type.SEMAPHORE)
public String getUser(@RequestParam("id") Integer id) throws InterruptedException {
logger.info("[getUser][id({})]", id);
Thread.sleep(10 * 1000L); // sleep 10 秒
return "User:" + id;
}

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

}

① 在 #getUser(Integer id) 方法,我们直接返回 "User:{id}",不进行任何逻辑。不过,这里为了模拟调用执行一定时长,通过 sleep 10 秒来实现。

② 在 #getUser(Integer id) 方法,添加了 Resilience4j 提供的 @Bulkhead 注解:

  • 通过 name 属性,设置对应的 Bulkhead 实例名为 "backendC",就是我们在「4.1 配置文件」中所添加的。
  • 通过 type 属性,设置 Bulkhead 类型为信号量的方式。
  • 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常

在请求被流控时,Resilience4j 不会执行 #getUser(Integer id) 方法,而是直接抛出 BulkheadFullException 异常,然后就进入 fallback 降级处理。

友情提示:注意,@Bulkhead 注解的 fallbackMethod 属性对应的 fallback 方法,不仅仅处理被流控时抛出的 BulkheadFullException 异常,也处理 #getUser(Integer id) 方法执行时的普通异常。

4.3 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

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

立马使用浏览器再次访问,会阻塞等待 < 5 秒左右,降级返回 mock:User:1。同时,我们在 IDEA 控制台的日志中,可以看到被流控时抛出的 BulkheadFullException 异常。

2020-05-20 07:39:17.181  INFO 84230 --- [nio-8080-exec-2] c.i.s.l.r.c.BulkheadDemoController       : [getUserFallback][id(1) exception(BulkheadFullException)]

友情提示:我们先来看线程池类型的 Bulkhead 示例。
友情提示:我们先来看线程池类型的 Bulkhead 示例。
友情提示:我们先来看线程池类型的 Bulkhead 示例。

4.4 配置文件

修改 application.yml 配置文件,添加 Resilience4j 信号量类型的 Bulkhead 相关配置项。

resilience4j:
# Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类
thread-pool-bulkhead:
instances:
backendD:
max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors()
core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1
queue-capacity: 100 # 线程池的队列大小。默认为 100
keep-alive-duration: 100s # 超过核心大小的线程,空闲存活时间。默认为 20 毫秒

resilience4j.thread-pool-bulkhead 是 Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类。

② 在 resilience4j.thread-pool-bulkhead.instances 配置项下,可以添加 Bulkhead 实例的配置,其中 key 为 Bulkhead 实例的名字value 为 Bulkhead 实例的具体配置,对应 ThreadPoolBulkheadProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendD" 的 Bulkhead,具体的每个项,胖友看看艿艿添加在后面的注释哈。

友情提示:这里我们设置 max-thread-pool-size 配置项为 1,仅允许并发调用数为 1,方便测试。

③ 在 resilience4j.thread-pool-bulkhead.configs 配置项下,可以添加通用配置项,提供给 resilience4j.thread-pool-bulkhead.instances Bulkhead 使用。示例如下图:

`resilience4j.thread-pool-bulkhead.configs` 示例

4.5 ThreadPoolBulkheadDemoController

创建 ThreadPoolBulkheadDemoController 类,提供示例 HTTP API 接口。代码如下:

@RestController
@RequestMapping("/thread-pool-bulkhead-demo")
public class ThreadPoolBulkheadDemoController {

@Autowired
private ThreadPoolBulkheadService threadPoolBulkheadService;

@GetMapping("/get_user")
public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException {
threadPoolBulkheadService.getUser0(id);
return threadPoolBulkheadService.getUser0(id).get();
}

@Service
public static class ThreadPoolBulkheadService {

private Logger logger = LoggerFactory.getLogger(ThreadPoolBulkheadService.class);

@Bulkhead(name = "backendD", fallbackMethod = "getUserFallback", type = Bulkhead.Type.THREADPOOL)
public CompletableFuture<String> getUser0(Integer id) throws InterruptedException {
logger.info("[getUser][id({})]", id);
Thread.sleep(10 * 1000L); // sleep 10 秒
return CompletableFuture.completedFuture("User:" + id);
}

public CompletableFuture<String> getUserFallback(Integer id, Throwable throwable) {
logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName());
return CompletableFuture.completedFuture("mock:User:" + id);
}

}


}

友情提示:这里创建了 ThreadPoolBulkheadService 的原因是,这里我们使用 Resilience4j 是基于注解 + AOP的方式,如果直接 this. 方式来调用方法,实际没有走代理,导致 Resilience4j 无法使用 AOP。

① 在 #getUser(Integer id) 方法,我们调用了 2 次 ThreadPoolBulkheadService 的 #getUser0(Integer id) 方法,测试在线程池 Bulkhead 下,且线程池大小为 1 时,被流控成“串行”执行。

② 在 #getUser0(Integer id) 方法,添加了 Resilience4j 提供的 @Bulkhead 注解:

  • 通过 name 属性,设置对应的 Bulkhead 实例名为 "backendC",就是我们在「4.4 配置文件」中所添加的。
  • 通过 type 属性,设置 Bulkhead 类型为线程池的方式。
  • 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常
  • 注意!!!方法的返回类型必须是 CompletableFuture 类型,包括 fallback 方法,否则会报异常,毕竟要提交线程池中执行。

在请求被流控时,Resilience4j 不会执行 #getUser0(Integer id) 方法,而是直接抛出 BulkheadFullException 异常,然后就进入 fallback 降级处理。不过艿艿测试了很久,都没触发抛出 BulkheadFullException 异常的情况,但是看 Resilience4j 源码又有这块逻辑,苦闷~

友情提示:注意,@Bulkhead 注解的 fallbackMethod 属性对应的 fallback 方法,不仅仅处理被流控时抛出的 BulkheadFullException 异常,也处理 #getUser0(Integer id) 方法执行时的普通异常。

4.6 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

调用 http://127.0.0.1:8080/thread-pool-bulkhead-demo/get_user?id=1 接口,可以通过日志看出,ThreadPoolBulkheadService 的 #getUser0(Integer id) 方法是“串行”执行的:

2020-05-20 8:25:24.059  INFO 85835 --- [head-backendD-1] DemoController$ThreadPoolBulkheadService : [getUser][id(1)]
2020-05-20 8:25:34.066 INFO 85835 --- [head-backendD-1] DemoController$ThreadPoolBulkheadService : [getUser][id(1)]

相互之间间隔 10 秒,因为我们在 #getUser0(Integer id) 方法是 sleep 了 10 秒。

5. Retry 重试

示例代码对应仓库:

Resilience4j 提供了 Retry 组件,在执行失败时,进行重试的行为。

下面,我们直接在「2. 快速入门」小节的基础上,直接增加 Resilience4j 重试相关的示例代码。改动点如下图所示:

项目调整

5.1 配置文件

修改 application.yml 配置文件,添加 Resilience4j Retry 相关配置项。

resilience4j:
# Resilience4j 的重试 Retry 配置项,对应 RetryProperties 属性类
retry:
instances:
backendE:
max-retry-Attempts: 3 # 最大重试次数。默认为 3
wait-duration: 5s # 下次重试的间隔,单位:微秒。默认为 500 毫秒
retry-exceptions: # 需要重试的异常列表。默认为空
ingore-exceptions: # 需要忽略的异常列表。默认为空

resilience4j.retry 是 Resilience4j 的 Retry 配置项,对应 RetryProperties 属性类。

② 在 resilience4j.retry.instances 配置项下,可以添加 Retry 实例的配置,其中 key 为 Retry 实例的名字value 为 Retry 实例的具体配置,对应 RetryProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendE" 的 Retry,具体的每个项,胖友看看艿艿添加在后面的注释哈。

③ 在 resilience4j.retry.configs 配置项下,可以添加通用配置项,提供给 resilience4j.retry.instances Retry 使用。示例如下图:

`resilience4j.retry.configs` 示例

5.2 RetryDemoController

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

@RestController
@RequestMapping("/retry-demo")
public class RetryDemoController {

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

@Autowired
private RestTemplate restTemplate;

@GetMapping("/get_user")
@Retry(name = "backendE", 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, throwable.getClass().getSimpleName());
return "mock:User:" + id;
}

}

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

② 在 #getUser(Integer id) 方法,添加了 Resilience4j 提供的 @Retry 注解:

  • 通过 name 属性,设置对应的 Retry 实例名为 "backendE",就是我们在「5.1 配置文件」中所添加的。
  • 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常

在多次重试失败到达上限时,Resilience4j 会抛出 MaxRetriesExceeded 异常,然后就进入 fallback 降级处理。

5.3 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

调用 http://127.0.0.1:8080/retry-demo/get_user?id=1 接口,可以通过日志看出,一共执行 + 重试 3 次,每次间隔 5 秒:

// 执行 + 重试共 3 次
2020-05-20 20:07:34.693 INFO 89536 --- [nio-8080-exec-1] c.i.s.l.r.c.RetryDemoController : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-20 20:07:39.722 INFO 89536 --- [nio-8080-exec-1] c.i.s.l.r.c.RetryDemoController : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-20 20:07:44.727 INFO 89536 --- [nio-8080-exec-1] c.i.s.l.r.c.RetryDemoController : [getUser][准备调用 user-service 获取用户(1)详情]

// 最终 fallback
2020-05-20 20:07:44.729 INFO 89536 --- [nio-8080-exec-1] c.i.s.l.r.c.RetryDemoController : [getUserFallback][id(1) exception(ResourceAccessException)]

6. TimeLimiter 限时器

示例代码对应仓库:

Resilience4j 提供了 TimeLimiter 组件,限制任务的执行时长,在超过时抛出异常。

下面,我们直接在「2. 快速入门」小节的基础上,直接增加 Resilience4j 限时器相关的示例代码。改动点如下图所示:

项目调整

6.1 配置文件

修改 application.yml 配置文件,添加 Resilience4j TimeLimiter 相关配置项。

resilience4j:
# Resilience4j 的超时限制器 TimeLimiter 配置项,对应 TimeLimiterProperties 属性类
timelimiter:
instances:
backendF:
timeout-duration: 1s # 等待超时时间,单位:微秒。默认为 1 秒
cancel-running-future: true # 当等待超时时,是否关闭取消线程。默认为 true

# Resilience4j 的线程池 Bulkhead 配置项,对应 ThreadPoolBulkheadProperties 属性类
thread-pool-bulkhead:
instances:
backendD:
max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors()
core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1
queue-capacity: 200 # 线程池的队列大小。默认为 100
keep-alive-duration: 100s # 超过核心大小的线程,空闲存活时间。默认为 20 毫秒

友情提示:因为 TimeLimiter 需要搭配线程池类型的 Bulkhead,所以这里添加了 resilience4j.thread-pool-bulkhead 配置项,因为 TimeLimiter 是基于线程池来实现超时限制的。

resilience4j.timelimiter 是 Resilience4j 的 TimeLimiter 配置项,对应 TimeLimiterProperties 属性类。

② 在 resilience4j.timelimiter.instances 配置项下,可以添加 Retry 实例的配置,其中 key 为 TimeLimiter 实例的名字value 为 TimeLimiter 实例的具体配置,对应 TimeLimiterConfigurationProperties.InstanceProperties 类。

这里,我们创建了一个实例名为 "backendF" 的 TimeLimiter,具体的每个项,胖友看看艿艿添加在后面的注释哈。

③ 在 resilience4j.timelimiter.configs 配置项下,可以添加通用配置项,提供给 resilience4j.timelimiter.instances Retry 使用。暂无示例,和其它配置项是类似的。

6.2 TimeLimiterDemoController

创建 TimeLimiterDemoController 类,提供示例 HTTP API 接口。代码如下:

@RestController
@RequestMapping("/time-limiter-demo")
public class TimeLimiterDemoController {

@Autowired
private TimeLimiterService timeLimiterService;

@GetMapping("/get_user")
public String getUser(@RequestParam("id") Integer id) throws ExecutionException, InterruptedException {
return timeLimiterService.getUser0(id).get();
}

@Service
public static class TimeLimiterService {

private Logger logger = LoggerFactory.getLogger(TimeLimiterService.class);

@Bulkhead(name = "backendD", type = Bulkhead.Type.THREADPOOL)
@TimeLimiter(name = "backendF", fallbackMethod = "getUserFallback")
public CompletableFuture<String> getUser0(Integer id) throws InterruptedException {
logger.info("[getUser][id({})]", id);
Thread.sleep(10 * 1000L); // sleep 10 秒
return CompletableFuture.completedFuture("User:" + id);
}

public CompletableFuture<String> getUserFallback(Integer id, Throwable throwable) {
logger.info("[getUserFallback][id({}) exception({})]", id, throwable.getClass().getSimpleName());
return CompletableFuture.completedFuture("mock:User:" + id);
}

}

}

友情提示:这里创建了 TimeLimiterService 的原因是,这里我们使用 Resilience4j 是基于注解 + AOP的方式,如果直接 this. 方式来调用方法,实际没有走代理,导致 Resilience4j 无法使用 AOP。

① 在 #getUser(Integer id) 方法,直接调用 ThreadPoolBulkheadService 的 #getUser0(Integer id) 方法进行返回。

② 在 #getUser0(Integer id) 方法,添加了 Resilience4j 提供的 @TimeLimiter 注解:

  • 通过 name 属性,设置对应的 Bulkhead 实例名为 "backendC",就是我们在「4.4 配置文件」中所添加的。
  • 通过 fallbackMethod 属性,设置执行发生 Exception 异常时,执行对应的 #getUserFallback(Integer id, Throwable throwable) 方法。注意,fallbackMethod 方法的参数要和原始方法一致,最后一个为 Throwable 异常
  • 注意!!!方法的返回类型必须是 CompletableFuture 类型,包括 fallback 方法,否则会报异常,毕竟要提交线程池中执行。

在请求执行超时时,Resilience4j 会抛出 TimeoutException 异常,然后就进入 fallback 降级处理。

友情提示:注意,@Bulkhead 注解的 fallbackMethod 属性对应的 fallback 方法,不仅仅处理被流控时抛出的 BulkheadFullException 异常,也处理 #getUser0(Integer id) 方法执行时的普通异常。

③ 在 #getUser0(Integer id) 方法上,添加了 Resilience4j 提供的 @Bulkhead 注解,并设置类型为线程池,原因上文我们也解释过了。

6.3 简单测试

执行 DemoApplication 启动 Resilience4j 示例项目。

调用 http://127.0.0.1:8080/time-limiter-demo/get_user?id=1 接口,可以通过日志看出,看到执行超时抛出 TimeoutException 异常,而后进入 fallback 服务降级,因此最终返回结果为 mock:User:1

2020-05-20 23:35:50.633  INFO 93020 --- [head-backendD-1] LimiterDemoController$TimeLimiterService : [getUser][id(1)]
2020-05-20 23:35:51.637 INFO 93020 --- [pool-2-thread-1] LimiterDemoController$TimeLimiterService : [getUserFallback][id(1) exception(TimeoutException)]

7. 执行顺序

我们在相同方法上,添加上述 Resilience4j 的注解,从而组合使用熔断、限流、舱壁、重试、重试的功能。例如说:

@CircuitBreaker(name = BACKEND, fallbackMethod = "fallback")
@RateLimiter(name = BACKEND)
@Bulkhead(name = BACKEND)
@Retry(name = BACKEND, fallbackMethod = "fallback")
@TimeLimiter(name = BACKEND)
public String method(String param1) {
throws new Exception("xxxx");
}

private String fallback(String param1, IllegalArgumentException e) {
return "test:IllegalArgumentException";
}

private String fallback(String param1, RuntimeException e) {
return "test:RuntimeException";
}

此时,我们就要注意它们的执行顺序是 :

Retry > Bulkhead > RateLimiter > TimeLimiter > Bulkhead

想要深入的胖友,可以进一步看看每个注解对应的切面实现,整理如下表格:

注解 切面 顺序
@Retry RetryAspect Ordered.LOWEST_PRECEDENCE - 4
@CircuitBreaker CircuitBreakerAspect Ordered.LOWEST_PRECEDENCE - 3
@RateLimiter RateLimiterAspect Ordered.LOWEST_PRECEDENCE - 2
@TimeLimiter TimeLimiterAspect Ordered.LOWEST_PRECEDENCE - 1
@Bulkhead BulkheadAspect Ordered.LOWEST_PRECEDENCE

当然,我们也可以通过 **-order 配置项,进行自定义执行顺序。修改方式如下图:

修改顺序

8. 监控端点

resilience4j-spring-boot2 基于 Spring Boot Actuator,提供了 Resilience4j 自定义监控端点,如下图所示:

Resilience4j 监控端点

主要分成三种类型:

友情提示:对 Spring Boot Actuator 不了解的胖友,可以后续阅读《芋道 Spring Boot 监控端点 Actuator 入门》文章。

下面,我们从上述小节的 lab-59-resilience4j-demo01 项目,复制出 lab-59-resilience4j-actuator 项目,接入 Spring Boot Actuator,实现监控相关功能。改动点如下图:

项目改动点

8.1 引入依赖

pom.xml 文件中,额外引入 Spring Boot Actuator 相关依赖。代码如下:

<!-- 实现对 Actuator 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

8.2 配置文件

修改 application.yaml 配置文件,增加 Spring Boot Actuator 配置项。配置如下:

management:
endpoints:
# Actuator HTTP 配置项,对应 WebEndpointProperties 配置类
web:
exposure:
include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。
endpoint:
# Health 端点配置项,对应 HealthProperties 配置类
health:
show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户;可选 ALWAYS 总是展示。
# 健康检查配置项
health:
circuitbreakers.enabled: true
ratelimiters.enabled: true

① 关于 Actuator 的配置项的作用,胖友看下艿艿添加的注释。如果还不理解的话,后续看下《芋道 Spring Boot 监控端点 Actuator 入门》文章。

② 默认情况下,Resilience4j 的 CircuitBreakersHealthIndicatorRateLimitersHealthIndicator 是关闭的,所以需要设置 management.health.circuitbreakers.enabledmanagement.health.ratelimiters.enabled 配置项为 true

同时,需要设置对应的 CircuitBreaker 和 RateLimiter 注册到 HealthIndicator 中。如下图所示:

HealthIndicator 注册


下面,我们执行 DemoApplication 启动项目。

8.3 Metrics endpoint

对应 《Resilience4j Metrics endpoint》 文档。

① 访问 http://127.0.0.1:8080/actuator/metrics 端点,可以看到 Resilience4j 提供的所有 Metrics 监控指标。如下图所示:

Resilience4j Metrics

② 访问 http://127.0.0.1:8080/actuator/metrics/resilience4j.bulkhead.core.thread.pool.size 地址,查看 resilience4j.bulkhead.core.thread.pool.size 监控指标,返回结果如下:

{
"name": "resilience4j.bulkhead.core.thread.pool.size",
"description": "The core thread pool size",
"baseUnit": null,
"measurements": [
{
"statistic": "VALUE",
"value": 1
}
],
"availableTags": [
{
"tag": "name",
"values": [
"backendD"
]
}
]
}

③ 一般情况下,我们可以通过 Prometheus 采集 Resilience4j 提供的 Metrics 监控数据,然后使用 Grafana 制作监控仪表盘

友情提示:对 Prometheus 和 Grafana 不了解的胖友,可以阅读《芋道 Prometheus + Grafana + Alertmanager 极简入门》文章。

同时,官方已经提供了 Grafana 的集成示范,可见 grafana_dashboard.json。另外,这里在推荐两篇外国小哥的文章:

8.4 Health endpoint

对应 《Resilience4j Health endpoint》 文档。

访问 http://127.0.0.1:8080/actuator/health 端点,可以看到 CircuitBreaker 和 RateLimiter 的健康状态。结果如下:

{
"status": "UP",
"components": {
"circuitBreakers": {
"status": "UP",
"details": {
"backendA": {
"status": "UP",
"details": {
"failureRate": "-1.0%",
"failureRateThreshold": "50.0%",
"slowCallRate": "-1.0%",
"slowCallRateThreshold": "100.0%",
"bufferedCalls": 0,
"slowCalls": 0,
"slowFailedCalls": 0,
"failedCalls": 0,
"notPermittedCalls": 0,
"state": "CLOSED"
}
}
}
},
"rateLimiters": {
"status": "UP",
"details": {
"backendB": {
"status": "UP",
"details": {
"availablePermissions": 1,
"numberOfWaitingThreads": 0
}
}
}
},
// ... 省略其它
}
}

8.5 Events endpoint

对应 《Resilience4j Events endpoint》 文档。

① 访问 http://127.0.0.1:8080/actuator/ 接口,可以看到每个类型的 Resilience4j 组件,都有自己的 Events endpoint。如下图所示:

Events endpoint

② 我们来测试下 CircuitBreaker 的 Event 事件。

首先,请求 http://127.0.0.1:8080/demo/get_user?id=1 接口,触发一次 fallback 降级,因为我们没有启动用户服务,所以调用显然会失败。

然后,请求 http://127.0.0.1:8080/actuator/circuitbreakerevents 地址,查看 CircuitBreaker 的 Event 事件。返回结果如下:

{
"circuitBreakerEvents": [
{
"circuitBreakerName": "backendA",
"type": "ERROR",
"creationTime": "2020-05-21T07:47:18.168+08:00[Asia/Shanghai]",
"errorMessage": "org.springframework.web.client.ResourceAccessException: I/O error on GET request for \"http://127.0.0.1:18080/user/get\": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)",
"durationInMs": 32,
"stateTransition": null
}
]
}

9. 集成到 Feign

resilience4j-feign 提供 Resilience4j 针对 Feign 的集成。

比较简单,胖友先自己看看《Resilience4j 官方文档 —— Feign》

10. 集成到 Dubbo

Resilience4j 暂未提供对 Dubbo 的集成。不过目前 Dubbo 已经提供了两种接入 Resilience4j 实现服务容错的方案:

代码不复杂,胖友自己瞅瞅很快就明白了。

666. 彩蛋

至此,我们已经完成了 Resilience4j 的入门。总的来说,因为Resilience4j 几个组件的拆分非常干净,所以理解起来还是蛮轻松的。

后续,胖友可以自己在看看《Resilience4j 官方文档》,进行下查漏补缺。也推荐阅读如下两篇文章:

另外,也超级推荐阅读下《限流熔断技术选型:从 Hystrix 到 Sentinel》文章,选型上的思考,特别是这张图,好评到不行啊!

文章目录
  1. 1. 1. 概述
  2. 2. 2. CircuitBreaker 熔断器
    1. 2.1. 2.1 搭建用户服务
    2. 2.2. 2.2 搭建 Resilience4j 示例项目
      1. 2.2.1. 2.2.1 引入依赖
      2. 2.2.2. 2.2.2 配置文件
      3. 2.2.3. 2.2.3 DemoController
      4. 2.2.4. 2.2.4 DemoApplication
    3. 2.3. 2.3 简单测试
  3. 3. 3. RateLimiter 限流器
    1. 3.1. 3.1 配置文件
    2. 3.2. 3.2 RateLimiterDemoController
    3. 3.3. 3.3 简单测试
  4. 4. 4. Bulkhead 舱壁
    1. 4.1. 4.1 配置文件
    2. 4.2. 4.2 BulkheadDemoController
    3. 4.3. 4.3 简单测试
    4. 4.4. 4.4 配置文件
    5. 4.5. 4.5 ThreadPoolBulkheadDemoController
    6. 4.6. 4.6 简单测试
  5. 5. 5. Retry 重试
    1. 5.1. 5.1 配置文件
    2. 5.2. 5.2 RetryDemoController
    3. 5.3. 5.3 简单测试
  6. 6. 6. TimeLimiter 限时器
    1. 6.1. 6.1 配置文件
    2. 6.2. 6.2 TimeLimiterDemoController
    3. 6.3. 6.3 简单测试
  7. 7. 7. 执行顺序
  8. 8. 8. 监控端点
    1. 8.1. 8.1 引入依赖
    2. 8.2. 8.2 配置文件
    3. 8.3. 8.3 Metrics endpoint
    4. 8.4. 8.4 Health endpoint
    5. 8.5. 8.5 Events endpoint
  9. 9. 9. 集成到 Feign
  10. 10. 10. 集成到 Dubbo
  11. 11. 666. 彩蛋