原创

Spring Retry优雅的实现重处理

作者:cndz 围观群众:677 更新于 标签:spring boot重处理Spring Retry

简介

在开发中,我们经常会遇到一些不可避免的错误,例如网络故障、磁盘空间不足等等。而这些错误可能会导致我们的业务逻辑失败,获取这些错误重试就可以成功,为了确保业务流程正常运行,需要进行重试操作。而Spring Retry就是为了解决这个问题而生的。

使用场景

Spring Retry主要用于处理一些不稳定的操作,比如网络请求、数据库连接等。在这些场景下,我们可能需要尝试多次才能成功,而Spring Retry正是为此而生的。

下面是一些适合使用Spring Retry的场景:

  1. 调用依赖服务时,由于网络不稳定等原因,可能会出现异常,需要重试才能成功。
  2. 执行某些任务时,可能会出现意外的异常,需要重试才能继续执行。
  3. 邮件短信发送场景。

Maven引入

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

使用

  1. 首先spring boot启动类添加启用重处理注解
//@EnableRetry 启用重处理注解
@EnableRetry
@SpringBootApplication
public class TestApplication {

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

}
  1. 使用

Spring Retry提供了非常简单易用的API,只需在需要重试的方法上添加注解即可。例如:

   private Integer callNum = 0 ;//调用次数

   @Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 1000))
    public void test() throws Exception {
        if(callNum == 0){
            callNum++;
            log.error("模拟失败情况,抛出异常,调用次数:{}",callNum);
            throw new IllegalArgumentException("错误了");
        }
        log.info("重调用成功!:调用次数{}",callNum);
    }

在上面的代码示例中,当内容抛出异常后,最多重试3次,每次重试间隔时间为1秒。下面为运行效果。重处理时未抛出异常。重处理成功。

运行结果效果图

简单解释下上面注解中常用值

  • value抛出指定异常进行出重处理操作。
  • includevalue一样,指定抛出异常后进行重处理,默认为空,默认为所有异常。
  • exclude 指定不用用处理的异常,如RuntimeException,如果我们主动校验发现了异常。不需要重处理,需要指定此值。
  • maxAttempts 该值指定最大重试次数,默认为3.

    @backoff 重试补偿策略,默认使用@Backoff

  • value 返回固定多少毫秒后重试(默认为1000毫秒)

  • delay返回延迟多少毫秒后重试(默认为0毫秒)
  • multiplier指定延迟的倍数,比如delay=5000l,multiplier=2时,第一次重试为5秒,第二次=上次延迟5秒乘以2倍10秒,第三次为上次延迟10秒乘以两倍20秒(上次延迟秒数乘以倍数)

注意:value与delay设置一个即可,如果有multiplier参数时,都会与multiplier结合使用。

@Recover 注解的使用

当重试到达指定次数时,被注解的方法将被回调,可以在该方法中进行日志处理。需要注意的是发生的异常和入参类型一致时才会回调。

若retrable方法中抛出了多个异常,Recover就需要写多个,或者recover中异常为Exception可以拦截所有异常回调

    @GetMapping("/test1")
    @Retryable(value = Exception.class,maxAttempts = 2,backoff = @Backoff(delay = 5000))
    public void test() throws Exception {
        callNum++;
        log.error("模拟失败情况,抛出异常,调用次数:{}",callNum);
        throw new IllegalArgumentException("错误了");
    }

    @Recover
    public void recover(Exception e){
        log.error("recover回调");
    }

执行结果如下:
代码效果演示

Spring Retry的优雅实现

然而,在实际应用中,我们往往需要更加灵活的重试策略,例如当遇到第一次重试时,我们需要将一些信息记录到日志中,或者在每次重试之前需要进行一些数据清理操作。这时,我们可以通过实现RetryCallbackRecoveryCallback接口来实现更加灵活的重试策略。例如:

public class MyRetryCallback implements RetryCallback<Void, Exception>, RecoveryCallback<Void> {
    private int retryCount = 0;

    @Override
    public Void doWithRetry(RetryContext context) throws Exception {
        try {
            // do something
        } catch (Exception e) {
            retryCount++;
            if (retryCount == 1) {
                // log some info
            }
            throw e;
        }
        return null;
    }

    @Override
    public Void recover(RetryContext context) throws Exception {
        // do some recovery operations
        return null;
    }
}

上面的代码实现了一个自定义的RetryCallbackRecoveryCallback接口,当重试失败时,会记录一些信息到日志中,并进行恢复操作。

Spring Retry的高级用法

除了注解和RetryCallbackRecoveryCallback接口外,Spring Retry还提供了一些高级用法,例如:

自定义Retryable注解

我们可以通过自定义Retryable注解来实现更加灵活的重试策略。例如:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Retryable(include = {IllegalArgumentException.class},
            exclude = {FileNotFoundException.class},
            maxAttempts = 3, backoff = @Backoff(delay = 1000))
public @interface MyRetryable {
}

上面的代码定义了一个自定义的Retryable注解,当方法抛出IllegalArgumentException异常时,进行重试,最多重试3次,每次重试之间间隔1秒。而当方法抛出FileNotFoundException异常时,则不进行重试。

自定义RetryTemplate

我们可以通过自定义RetryTemplate来实现更加灵活的重试策略。例如:

public class MyRetryTemplate extends RetryTemplate {
    public MyRetryTemplate() {
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);
        setRetryPolicy(retryPolicy);

        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(1000);
        setBackOffPolicy(backOffPolicy);
    }
}

上面的代码定义了一个自定义的RetryTemplate,最多重试3次,每次重试之间间隔1秒。

::: 提示 注意事项

在使用Spring Retry时,需要注意过多的重试可能会导致性能问题甚至加剧原始问题。确保为最大重试次数和重试之间的延迟设置适当的值。此外,请注意某些异常(如RuntimeException)默认情况下会进行重试。如果这些异常不适用于您的用例,请考虑将其排除在外。

:::

注意事项

在使用Spring Retry时,需要注意以下几点:

  1. 避免无限重试。设置合适的最大重试次数和重试间隔时间,避免无限重试。
  2. 不要在@Transactional方法上使用@Retryable注解。由于@Transactional注解会影响方法的事务处理,因此不能与@Retryable注解一起使用。
  3. 只重试受检查异常。默认情况下,Spring Retry只会重试受检查异常,而不会重试运行时异常。
  4. 需要额外注意的一点是重处理通过Aop来实现,方法内调用重处理不会起作用.

结论

Spring Retry是一个非常好用的重试框架,它可以轻松地帮助我们解决重试的问题。通过使用注解、自定义RetryCallbackRecoveryCallback接口、自定义Retryable注解和自定义RetryTemplate,我们可以实现更加灵活的重试策略,从而为我们的应用带来更好的容错性和可靠性。