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

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


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

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

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

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

1. 概述

《芋道 Spring Boot 服务容错 Resilience4j 入门》中,我们已经学习了如何在 Spring Boot 中,使用 Resilience4j 实现服务容错。

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

Resilience4j 自己提供了 resilience4j-spring-cloud2 项目,实现在 Spring Cloud 中,使用 Resilience4j 实现服务容错。不过 resilience4j-spring-cloud2 暂时只是在 resilience4j-spring-boot2 项目的基础上的进一步封装,接入 Spring Cloud Context 提供的 RefreshScope 机制。如下图所示:

`resilience4j-spring-cloud2` 项目

因此,在使用上 99.99%一模一样。所以,胖友需要先阅读完《芋道 Spring Boot 服务容错 Resilience4j 入门》文章哈~

2. resilience4j-spring-cloud2 使用示例

示例代码对应仓库:

偷个小懒,艿艿就不专门写 resilience4j-spring-cloud2 的使用教程。胖友只需要参考《芋道 Spring Boot 服务容错 Resilience4j 入门》文章,将 resilience4j-spring-boot2 依赖替换resilience4j-spring-cloud2 依赖即可。如下图所示:

改动点

当然,老实巴巴的艿艿,还是创建了 labx-24-resilience4j-demo01 示例项目,胖友也可以运行下哈~良心如我!

3. Spring Cloud Circuit Breaker

示例代码对应仓库:

Spring Cloud Circuit Breaker 是 Spring Cloud 定义的熔断器统一抽象模型 CircuitBreaker。它希望提供一致的熔断器使用的 API 方式,方便我们选择和切换具体的熔断器的实现。

Spring Cloud Circuit breaker provides an abstraction across different circuit breaker implementations.

It provides a consistent API to use in your applications allowing you the developer to choose the circuit breaker implementation that best fits your needs for your app.

目前它已经提供四种熔断器的实现:

框架 Spring Cloud Circuit Breaker
Hystrix HystrixCircuitBreaker
Resilience4J Resilience4JCircuitBreaker
Sentinel SentinelCircuitBreaker
Spring Retry SpringRetryCircuitBreaker

下面,我们就来搭建一个使用 Spring Cloud Circuit Breaker Resilience4J 的示例项目,最终如下图所示:

项目结构

3.1 引入依赖

创建 pom.xml 文件,重点引入 spring-cloud-starter-circuitbreaker-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>labx-24-resilience4j-demo02</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>

<!-- 引入 Spring Cloud CircuitBreaker Resilience4j 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
<version>1.0.2.RELEASE</version>
</dependency>

</dependencies>

</project>

3.2 配置类

创建 Resilience4jConfig 配置类,自定义 Resilience4JCircuitBreakerFactory Bean,它是负责创建 Resilience4JCircuitBreaker 的工厂。代码如下:


@Configuration
public class Resilience4jConfig {

@Bean
public Customizer<Resilience4JCircuitBreakerFactory> resilience4JCircuitBreakerFactoryCustomizer() {
return new Customizer<Resilience4JCircuitBreakerFactory>() {

@Override
public void customize(Resilience4JCircuitBreakerFactory resilience4JCircuitBreakerFactory) {
// 设置默认的配置
resilience4JCircuitBreakerFactory.configureDefault(new Function<String, Resilience4JConfigBuilder.Resilience4JCircuitBreakerConfiguration>() {

@Override
public Resilience4JConfigBuilder.Resilience4JCircuitBreakerConfiguration apply(String id) {
// 创建 TimeLimiterConfig 对象
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.ofDefaults(); // 默认
// 创建 CircuitBreakerConfig 对象
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.ofDefaults(); // 默认
// 创建 Resilience4JCircuitBreakerConfiguration 对象
return new Resilience4JConfigBuilder(id)
.timeLimiterConfig(timeLimiterConfig)
.circuitBreakerConfig(circuitBreakerConfig)
.build();
}

});
// 设置编号为 "slow" 的自定义配置
resilience4JCircuitBreakerFactory.configure(new Consumer<Resilience4JConfigBuilder>() {
@Override
public void accept(Resilience4JConfigBuilder resilience4JConfigBuilder) {
// 创建 TimeLimiterConfig 对象
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)) // 自定义
.build();
// 创建 CircuitBreakerConfig 对象
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() // 自定义
.slidingWindow(5, 5, CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
.build();
// 设置 Resilience4JCircuitBreakerConfiguration 对象
resilience4JConfigBuilder
.timeLimiterConfig(timeLimiterConfig)
.circuitBreakerConfig(circuitBreakerConfig);
}
}, "slow");
}

};
}

}

因为套娃比较厉害,我们来截图 + 标记的方式,解释一下下。如下图所示:

Resilience4jConfig 图

友情提示:Spring Cloud Circuit Breaker 暂时木有提供配置文件进行自定义的方式。

3.3 DemoController

创建 DemoController 类,提供 CircuitBreaker 的使用示例示例。代码如下:

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

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

@Autowired
private RestTemplate restTemplate;

@Autowired
private CircuitBreakerFactory circuitBreakerFactory;

@GetMapping("/get_user")
public String getUser(@RequestParam("id") Integer id) {
// <1>
return circuitBreakerFactory.create("slow").run(new Supplier<String>() { // <2.1>

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

}, new Function<Throwable, String>() { // <2.2>

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

});
}
}

① 调用 CircuitBreakerFactory#create(String id) 方法,创建一个 CircuitBreaker 对象。其中,id 参数指的是使用哪个编号的 CircuitBreaker,这里我们设置为 slow,就是我们在「3.2 配置类」中所自定义的。

② CircuitBreaker 提供了 #run(...) 方法如下:

// CircuitBreaker.java

<T> T run(Supplier<T> toRun, Function<Throwable, T> fallback);

因此,我们在 <2.1> 提供了执行逻辑的实现,在 <2.2> 提供 fallback 的实现。

3.5 DemoApplication

创建 DemoApplication 类,示例启动类。代码如下:

@SpringBootApplication
public class DemoApplication {

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

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

}

3.4 简单测试

使用 DemoApplication 启动示例项目。

① 请求 http://127.0.0.1:8080/demo/get_user?id=1 地址,返回结果为 mock:User:1。在 IDEA 控制台我们可以看到 fallback 逻辑执行的日志:

2020-05-22 08:08:08.729  INFO 25726 --- [pool-1-thread-1] c.i.s.l.r.controller.DemoController      : [getUser][准备调用 user-service 获取用户(1)详情]

// fallback
2020-05-22 08:08:08.754 INFO 25726 --- [nio-8080-exec-1] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1)exception(ResourceAccessException)]

快速请求 6http://127.0.0.1:8080/demo/get_user?id=1 地址,触发 CircuitBreaker 熔断。在 IDEA 控制台我们可以看到 fallback 逻辑执行的日志:

2020-05-22 08:11:02.117  INFO 25796 --- [pool-1-thread-1] c.i.s.l.r.controller.DemoController      : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-22 08:11:02.143 INFO 25796 --- [nio-8080-exec-1] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(ResourceAccessException)]
2020-05-22 08:11:02.528 INFO 25796 --- [pool-1-thread-1] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-22 08:11:02.529 INFO 25796 --- [nio-8080-exec-2] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(ResourceAccessException)]
2020-05-22 08:11:02.644 INFO 25796 --- [pool-1-thread-1] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-22 08:11:02.645 INFO 25796 --- [nio-8080-exec-3] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(ResourceAccessException)]
2020-05-22 08:11:02.727 INFO 25796 --- [pool-1-thread-1] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-22 08:11:02.728 INFO 25796 --- [nio-8080-exec-4] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(ResourceAccessException)]
2020-05-22 08:11:02.810 INFO 25796 --- [pool-1-thread-1] c.i.s.l.r.controller.DemoController : [getUser][准备调用 user-service 获取用户(1)详情]
2020-05-22 08:11:02.816 INFO 25796 --- [nio-8080-exec-5] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(ResourceAccessException)]

// 熔断,直接 fallback
2020-05-22 08:11:02.896 INFO 25796 --- [nio-8080-exec-6] c.i.s.l.r.controller.DemoController : [getUserFallback][id(1) exception(CallNotPermittedException)]

3.5 接入 Spring Cloud Gateway

在 Spring Cloud Gateway 中,提供了 SpringCloudCircuitBreakerFilter 过滤器,接入 Spring Cloud Circuit Breaker 实现熔断的功能。这样,网关就可以方便切换和使用不同的 CircuitBreaker 具体实现框架。

这里我们暂时不拓展开,胖友可以去看看 spring-cloud-gateway-resilience4j 项目的示例代码,搭配《Spring cloud gateway with Resilience4j circuit breaker》文章。

6. 彩蛋

暂时木有彩蛋~暂时还是比较推荐使用 resilience4j-spring-cloud2 的方式,功能更强大,编码更舒服~

至于 Spring Cloud Circuit Breaker 呢,继续跟进下它的演进,看看后续会变得咋样~

文章目录
  1. 1. 1. 概述
  2. 2. 2. resilience4j-spring-cloud2 使用示例
  3. 3. 3. Spring Cloud Circuit Breaker
    1. 3.1. 3.1 引入依赖
    2. 3.2. 3.2 配置类
    3. 3.3. 3.3 DemoController
    4. 3.4. 3.5 DemoApplication
    5. 3.5. 3.4 简单测试
    6. 3.6. 3.5 接入 Spring Cloud Gateway
  4. 4. 6. 彩蛋