Spring 注解@Retryable解决循环重试功能

SpringRetry是一个模块,通过注解方式提供重试机制。@Retryable用于标记可重试的方法,配置如最大尝试次数、重试间隔等。当重试次数耗尽,可通过@Recover注解定义回调方法进行异常恢复或日志记录。注意,基于AOP,不支持类内方法自调用,且回调方法需与重试方法在同一类中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在实际工作中,重处理是一个非常常见的场景,比如:
发送消息失败。
调用远程服务失败。
争抢锁失败。
这些错误可能是因为网络波动造成的,等待过后重处理就能成功。通常来说,会用try/catch,while循环之类的语法来进行重处理,但是这样的做法缺乏统一性,并且不是很方便,要多写很多代码。然而spring-retry却可以通过注解,在不入侵原有业务逻辑代码的方式下,优雅的实现重处理功能。

@Retryable是什么?

spring系列的spring-retry是另一个实用程序模块,可以帮助我们以标准方式处理任何特定操作的重试。在spring-retry中,所有配置都是基于简单注释的。

 <dependency>
  <groupId>org.springframework.retry</groupId>
  <artifactId>spring-retry</artifactId>
 </dependency>

启用@Retryable

@EnableRetry
@SpringBootApplication
public class HelloApplication {

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

}

在方法上添加@Retryable

import com.mail.elegant.service.TestRetryService;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.time.LocalTime;
 
@Service
public class TestRetryServiceImpl implements TestRetryService {
 
    @Override
    @Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
    public int test(int code) throws Exception{
        System.out.println("test被调用,时间:"+LocalTime.now());
          if (code==0){
              throw new Exception("情况不对头!");
          }
        System.out.println("test被调用,情况对头了!");
 
        return 200;
    }
}

来简单解释一下注解中几个参数的含义:
value:抛出指定异常才会重试
include:和value一样,默认为空,当exclude也为空时,默认所有异常
exclude:指定不处理的异常
maxAttempts:最大重试次数,默认3次
backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。

当重试耗尽时还是失败,会出现什么情况呢?
当重试耗尽时,RetryOperations可以将控制传递给另一个回调,即RecoveryCallback。Spring-Retry还提供了@Recover注解,用于@Retryable重试失败后处理方法。如果不需要回调方法,可以直接不写回调方法,那么实现的效果是,重试次数完了后,如果还是没成功没符合业务判断,就抛出异常。

@Recover

@Recover
public int recover(Exception e, int code){
   System.out.println("回调方法执行!!!!");
   //记日志到数据库 或者调用其余的方法
    return 400;
}

可以看到传参里面写的是 Exception e,这个是作为回调的接头暗号(重试次数用完了,还是失败,我们抛出这个Exception e通知触发这个回调方法)。对于@Recover注解的方法,需要特别注意的是:
方法的返回值必须与@Retryable方法一致
方法的第一个参数,必须是Throwable类型的,建议是与@Retryable配置的异常一致,其他的参数,需要哪个参数,写进去就可以了(@Recover方法中有的)
该回调方法与重试方法写在同一个实现类里面

注意事项
由于是基于AOP实现,所以不支持类里自调用方法
如果重试失败需要给@Recover注解的方法做后续处理,那这个重试的方法不能有返回值,只能是void
方法内不能使用try catch,只能往外抛异常
@Recover注解来开启重试失败后调用的方法(注意,需跟重处理方法在同一个类中),此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。

<think>好的,用户之前问过关于@Retryable注解的参数默认值,现在想知道如何在Spring Boot中不借助这个注解,手动实现异常重试机制。我需要先理解用户的需求。可能用户的项目中无法使用注解,或者他们想更灵活地控制重试逻辑,或者只是出于学习的目的想了解底层实现。 首先,我得回忆一下Spring Retry的工作原理。@Retryable是基于AOP的,所以手动实现的话可能需要自己编写重试逻辑,可能用循环结构,配合异常捕获,然后加上延迟策略,比如Thread.sleep或者使用ScheduledExecutorService。同时,可能需要考虑重试次数、退避策略、异常类型过滤等问题。 用户的需求是手写重试机制,所以需要给出不同的实现方法。可能需要分步骤讲解,比如使用简单循环、结合Spring的RetryTemplate,或者响应式编程中的重试方式。同时,需要比较不同方法的优缺点,帮助用户选择适合的方案。 接下来,我需要考虑用户可能的背景。他们可能已经了解Spring的基本知识,但对RetryTemplate不太熟悉,或者对函数式编程有接触。需要确保代码示例清晰,解释每一步的作用,比如如何设置重试次数、退避策略、异常处理等。 还要注意提到手动实现需要注意的问题,比如事务管理,因为重试可能会引起事务的重复提交,需要将业务逻辑放在独立的事务方法中。此外,可能需要提到性能问题,比如重试时的延迟是否会影响系统响应,以及如何合理设置最大尝试次数和退避策略,避免无限重试或资源耗尽。 另外,用户可能在分布式环境中使用,手动重试可能需要考虑幂等性,但这个问题可能超出了当前问题的范围,不过稍微提一下会更好,让用户知道需要考虑的额外因素。 现在要组织回答的结构,先介绍不同的实现方式,比如基础循环、RetryTemplate、响应式重试。每个部分给出示例代码,并解释关键点。最后总结各自的优缺点和适用场景,帮助用户做出选择。 需要确保代码示例正确,比如在循环方法中,正确捕获异常,累计重试次数,正确使用退避策略。在RetryTemplate的例子中,正确配置重试策略和退避策略,并展示如何使用模板执行代码。响应式编程部分可能需要使用Reactor的retry方法,并展示如何链式调用。 最后,提醒用户注意事项,比如事务管理、异常处理、资源释放等,确保手动重试不会引入新的问题。可能还需要提到Spring Retry的优势,比如声明式注解的简洁性,但手写提供了更多的灵活性。 总结下来,回答需要结构清晰,分点讲解不同方法,代码示例详细,注意事项全面,帮助用户全面理解如何在不使用@Retryable的情况下实现重试机制。</think>在 Spring Boot 中,如果不使用 `@Retryable` 注解,可以通过以下方式手动实现异常重试机制: --- ### 一、基础实现方案(纯代码控制) #### 1. **简单循环重试** ```java public void retryableMethod() { int maxAttempts = 3; int currentAttempt = 0; long delayMs = 1000; while (currentAttempt < maxAttempts) { try { // 业务逻辑(如调用外部服务) performBusinessLogic(); return; // 成功则退出 } catch (Exception e) { currentAttempt++; if (currentAttempt >= maxAttempts) { throw new RuntimeException("重试耗尽", e); } // 延迟策略(如固定间隔) try { Thread.sleep(delayMs); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } } } ``` #### 2. **带指数退避的循环重试** ```java public void retryWithBackoff() { int maxAttempts = 3; long initialDelay = 1000; double multiplier = 2; for (int attempt = 1; attempt <= maxAttempts; attempt++) { try { performBusinessLogic(); return; } catch (Exception e) { if (attempt == maxAttempts) { throw new RuntimeException("重试耗尽", e); } // 计算退避时间(指数递增) long delay = (long) (initialDelay * Math.pow(multiplier, attempt - 1)); try { Thread.sleep(delay); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } } } ``` --- ### 二、使用 Spring Retry 的 `RetryTemplate` 即使不依赖注解Spring Retry 库仍提供了编程式 API `RetryTemplate`,更易管理复杂策略。 #### 1. **配置 `RetryTemplate`** ```java @Configuration public class RetryConfig { @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = new RetryTemplate(); // 重试策略(最多3次,针对特定异常) SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); retryPolicy.setMaxAttempts(3); retryTemplate.setRetryPolicy(retryPolicy); // 退避策略(初始1秒,指数递增) ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); backOffPolicy.setInitialInterval(1000); backOffPolicy.setMultiplier(2); retryTemplate.setBackOffPolicy(backOffPolicy); return retryTemplate; } } ``` #### 2. **使用 `RetryTemplate`** ```java @Autowired private RetryTemplate retryTemplate; public void executeWithRetry() { retryTemplate.execute(context -> { performBusinessLogic(); return null; }); } ``` --- ### 三、响应式编程中的重试(WebFlux) 若使用 Spring WebFlux,可通过 Reactor 的 `retry` 操作符实现: ```java public Mono<Void> reactiveRetry() { return Mono.fromRunnable(this::performBusinessLogic) .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)) .maxBackoff(Duration.ofSeconds(10)) .filter(ex -> ex instanceof IOException)); } ``` --- ### 四、关键注意事项 1. **事务管理** 如果业务逻辑涉及数据库事务,需将重试代码包裹在事务外部,避免重复提交: ```java @Transactional(propagation = Propagation.NOT_SUPPORTED) // 禁用事务 public void retryWrapper() { retryableMethod(); } ``` 2. **异常过滤** 明确捕获特定异常类型,避免重试不可恢复的错误(如 `NullPointerException`)。 3. **资源释放** 确保重试过程中资源(如连接、文件句柄)被正确释放。 --- ### 五、方案对比 | 方案 | 优点 | 缺点 | |-------------------|-----------------------------|-----------------------------| | 简单循环 | 无需依赖库,代码透明 | 策略简单,扩展性差 | | `RetryTemplate` | 策略灵活,集成 Spring 生态 | 需引入 `spring-retry` 依赖 | | 响应式重试 | 非阻塞,适合高并发场景 | 仅适用于响应式编程模型 | --- 根据场景选择合适方案: - **简单需求**:直接使用循环重试 - **复杂策略**:优先选择 `RetryTemplate` - **异步/高并发**:响应式编程 + Reactor 的 `retry`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值