接口调用总失败?试试Spring官方重试框架Spring-Retry

关注我的公众号:【编程朝花夕拾】,可获取首发内容。

01 引言

在现代分布式系统和微服务架构中,服务间的网络调用变得无比频繁。然而,网络是不可靠的,它可能因为瞬时故障(如网络抖动、服务短暂过载、资源临时不可用等)导致调用失败。简单地一失败就放弃,会严重降低系统的健壮性和用户体验尤其调用第三方接口时表现最明显。

因此,重试机制 成为了构建 resilient(弹性)系统不可或缺的一环。Spring Retry 作为 Spring 生态系统中的一员,提供了一种声明式、注解驱动的高效重试解决方案,能让你的代码在应对短暂故障时变得更加健壮和优雅。

02 简介

Spring Retry 的核心思想是 AOP(面向切面编程) 。它通过代理(Proxy)机制,在目标方法的外部包裹一层重试逻辑。

当你调用一个被 @Retryable 注解的方法时,实际上是在调用一个由 Spring 生成的代理对象,这个代理对象负责在方法执行失败时,根据你配置的策略进行重试。

03 最佳实践

因为Spring Retry的核心思想是AOP,所以声明式重试需要依赖AOP

3.1 Maven依赖

Spring Boot项目依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

如果是传统的Spring项目,则需要如下依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>2.0.5</version> <!-- 请使用最新版本 -->
</dependency>
<!-- Spring AOP 依赖 (Spring Boot项目一般已包含) -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
</dependency>
<!-- 如果你使用@Recover,还需要AspectJ的依赖 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

3.2 启用重试功能

@EnableRetry

在项目的配置类或者主启动类上添加 @EnableRetry 注解,以启用 Spring Retry 的自动代理功能。

java 复制代码
@SpringBootApplication
@EnableRetry
public class RetryApplication {

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

3.3 方法添加@Retryable

@Retryable是最核心的注解,用于标注哪些方法在发生异常时需要重试。

基本参数说明:

  • value / include: 指定需要重试的异常类型数组。默认为空(重试所有异常)。
  • maxAttempts: 最大重试次数(包括第一次调用)。例如设置为 3,意味着最多会调用 3 次方法(1次初始调用 + 2次重试)。
  • backoff: 设置重试退避策略,通常与 @Backoff 注解联用。
  • recover: 服务降级的方法。

3.4 案例代码

我们通过传递参数status来控制异常,重试参数:

  • retryFor = RuntimeException.class:只针对RuntimeException重试。
  • maxAttempts = 4:包含第一次请求总共请求4次
  • delay = 1000:延迟1s重试
  • multiplier = 2.0:延迟时间的乘数(用于指数退避),步长,延迟序列为:1s, 2s, 4s...
java 复制代码
@Service
public class RetryServiceImpl implements RetryService{

    @Override
    @Retryable(retryFor = RuntimeException.class,
                maxAttempts = 4,
                backoff = @Backoff(delay = 1000,
                        multiplier = 2.0
                )
    )
    public String invokeMethod(Integer status) {
        System.out.println("invokeMethod 调用时间:"
                + LocalDateTime.now()
                .format(DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss")));
        // 模拟异常
        if (status != null) {
            throw new RuntimeException("服务中断了......");
        }
        return "SUCCESS";
    }
}

效果:

3.5 服务降级

如果重试完成之后还是响应失败,那么就可以服务降级,返回指定的数据。

这是需要一个注解@Recover来指定降级的方法。

这里的方法名可以随意取,默认会被@Retryable识别,也可以通过recover参数指定方法。

@Recover方法不被执行的原因:

  • 未加@Recover注解

  • recover参数指定错误

  • @Recover修改的方法参数必须是retryFor的异常以及原方法的参数。

    如:

    java 复制代码
    @Retryable(retryFor = RuntimeException.class)
    public String test(Integer status) {
        // ...  
    }
    
    @Recover
    public String testRecover(RuntimeException e, Integer status) {
      	// ...
    }

04 编程式重试

编程式重试的核心是RetryTemplate,使用更加灵活,不受AOP限制。

java 复制代码
@Test
void test() throws Throwable {
    RetryTemplate retryTemplate = RetryTemplate.builder()
            .maxAttempts(4)
            .exponentialBackoff(1000, 2, 10000)
            .retryOn(RuntimeException.class)
            .build();


    String execute = retryTemplate.execute(
            (RetryCallback<String, Throwable>) context -> {
        RetryService retryService = new RetryServiceImpl();
        return retryService.invokeMethod(1);
    }, context -> "服务降级了!");

    System.out.println(execute);
}

效果:

05 小结

Spring Retry 提供了 声明式编程式 两种风格来满足不同场景的需求:

  • 无脑上注解 :对于大多数通用的重试场景,使用 @Retryable + @Backoff + @Recover 是最高效、最清晰的选择。
  • 动手写代码 :当你需要极致灵活动态控制 时,RetryTemplate 配合各种 RetryPolicyBackOffPolicy 是你的不二之选。

快去试试水吧!

相关推荐
DKPT42 分钟前
JVM栈溢出和堆溢出哪个先满?
java·开发语言·jvm·笔记·学习
m0_4750645042 分钟前
jvm双亲委派的含义
java·jvm
海梨花1 小时前
今日八股——JVM篇
jvm·后端·面试
毕设源码-朱学姐5 小时前
【开题答辩全过程】以 爱心捐赠网站为例,包含答辩的问题和答案
java·eclipse
Pr Young5 小时前
服务优雅停止和服务优雅启动
后端
嘟嘟MD7 小时前
程序员副业 | 2025年9月复盘
后端·aigc
尘觉7 小时前
中秋节与 Spring Boot 的思考:一场开箱即用的团圆盛宴
java·spring boot·后端
间彧8 小时前
Seata分布式事务框架详解与项目实战
后端
Le1Yu8 小时前
2025-10-7学习笔记
java·笔记·学习
zhuyasen8 小时前
单机已达上限?PerfTest 分布式压测登场,轻松模拟百万用户洪峰
后端·性能优化·测试