共计 3425 个字符,预计需要花费 9 分钟才能阅读完成。
在现代微服务架构中,服务间调用失败在所难免,如何优雅地失败,并在故障恢复后自动重试,是保障系统稳定性的关键一环。本文将深入讲解如何结合 Spring Retry
与 Resilience4j
实现强大且可配置的熔断+重试机制
一、为什么要结合使用 Spring Retry 与 Resilience4j?
组件 | 主要能力 | 使用价值 |
---|---|---|
Spring Retry | 提供声明式重试机制 | 网络抖动、瞬时失败时快速恢复 |
Resilience4j | 熔断、限流、隔离、缓存、重试等 | 快速故障切断,保护下游系统 |
单独使用 Spring Retry 只关注重试逻辑,而 Resilience4j 提供更丰富的容错策略。组合使用可实现:
- 先熔断,避免无意义的重试;
- 再根据策略精确重试,提高可用性。
二、引入依赖(Spring Boot 3.x)
<!-- Resilience4j 核心与 Spring Boot 整合 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
</dependency>
<!-- Spring Retry 支持 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<!-- Spring AOP,用于 @Retryable 注解生效 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
三、核心配置
application.yml 示例
resilience4j:
circuitbreaker:
instances:
backendService:
failure-rate-threshold: 50 # 失败比例超过 50% 触发熔断
sliding-window-type: COUNT_BASED # 使用调用次数窗口
sliding-window-size: 10 # 窗口大小为10次请求
minimum-number-of-calls: 5 # 最小请求次数,熔断器才开始评估状态
wait-duration-in-open-state: 10s # OPEN 状态维持时间
permitted-number-of-calls-in-half-open-state: 3 # HALF_OPEN 状态允许3次尝试
retry:
instances:
backendService:
max-attempts: 3 # 最大重试次数(含第一次调用)
wait-duration: 2s # 重试间隔时间
retry-exceptions: # 哪些异常触发重试
- java.io.IOException
- java.net.SocketTimeoutException
四、实战案例:远程服务调用容错设计
我们模拟一个远程服务调用(如商品查询接口),使用 @CircuitBreaker
与 @Retryable
配合,优雅降级。
1. 服务接口类
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.retry.annotation.Retry;
import org.springframework.stereotype.Service;
import java.util.Random;
@Service
public class ProductService {
/**
* 获取商品信息,结合熔断器与重试机制
* - 熔断器名称为 backendService,熔断状态将短路请求
* - 如果请求失败,则进行最多3次的重试
*/
@CircuitBreaker(name = "backendService", fallbackMethod = "fallback")
@Retry(name = "backendService")
public String getProductInfo(String productId) {
// 模拟远程接口波动,随机失败
if (new Random().nextBoolean()) {
throw new RuntimeException("远程服务失败");
}
// 正常返回商品信息
return "商品信息:" + productId;
}
/**
* 熔断或重试都失败后进入 fallback 方法
* 注意:fallback 方法参数需和原方法一致,外加 Throwable 参数
*/
public String fallback(String productId, Throwable ex) {
// 返回默认值或错误提示
return "服务降级,返回默认商品信息";
}
}
2. 控制器入口
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/{id}")
public String getProduct(@PathVariable String id) {
return productService.getProductInfo(id);
}
}
五、执行流程剖析(高级视角)
调用顺序如下:
- @CircuitBreaker:首先判断熔断器状态:
- OPEN:直接走 fallback;
- HALF_OPEN:试探调用;
- CLOSED:允许调用。
- @Retry:只有在熔断器允许通过后,才触发 Retry。
- 异常传播:
- 若熔断,直接触发 fallback;
- 若失败达到 retry 次数后仍失败,传播异常或 fallback。
注意:执行顺序是 CircuitBreaker → Retry,不是相反!
六、源码分析(Resilience4j)
以 CircuitBreakerAspect
为例(使用 AOP 拦截):
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 实际上会调用 CircuitBreakerAspect 的拦截逻辑
CircuitBreaker circuitBreaker = registry.circuitBreaker("backendService");
Callable<Object> callable = () -> invocation.proceed(); // 原始方法调用被包裹
// 包裹逻辑,内部会根据熔断状态决定是否执行 callable
return circuitBreaker.executeCallable(callable);
}
再看 retry 的代理逻辑(Spring Retry):
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.execute(context -> {
return invocation.proceed();
}, recoveryCallback);
关键点:
CircuitBreakerAspect
和RetryAspect
都是通过 AOP 拦截原方法;- Resilience4j 是通过
decorateCheckedSupplier
或decorateCallable
包裹; - Spring Retry 是通过
RetryTemplate.execute()
执行重试逻辑。
七、集成 Micrometer 实现监控
management:
endpoints:
web:
exposure:
include: resilience4j.*
集成 Prometheus 后可实时观察熔断次数、状态变化。
八、总结
通过 Spring Retry + Resilience4j
的组合使用,可以实现如下目标:
- 降低外部依赖波动带来的故障风险
- 精细控制失败重试与熔断策略
- 降级 fallback 提升系统稳定性
- 结合监控快速定位问题
🔍 延伸阅读
如果本文对你有帮助,欢迎点赞、评论!我是 李卷卷,专注Java相关知识输出。感谢阅读!
正文完