Spring优雅的异常重试


1. 基本介绍

在业务常见开发中经常我们会涉及到失败重试的逻辑,如当发送通知失败后再次尝试。

针对此类场景最简单粗暴的方式即 CV 大法,但显然这种方式不够优雅,为此 Spring Boot 中提供了 Retry 从而实现优雅的异常失败重试。

在开始前在 Spring Boot 工程中引入相应的 Retry 依赖。

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

2. 异常处理

Spring Retry 使用也并不复杂,通过 @Retryable 注解配置,相关配置参考下表。

参数 描述
value 配置何种情况重试。
include 配置触发重试情况的异常类型。
exclude 配置不触发重试情况的异常类型。
maxAttempts 通过配置尝试次数,默认为 3。
maxAttemptsExpression 通过表达式配置尝试次数,默认为 3。
backoff 通过表达式配置间隔时间。
recover 指定重试后仍失败触发的方法。

如下示例代码中则设置当 call() 方法抛出 RuntimeException 异常时重试两次,每次重试间隔 500 毫秒。

@Retryable 除了注解中相关参数除常量定义外,同时支持 ${} 表达式方式读取配置文件中内容。

@Component
public class RetryService {

    @Retryable(
            value = RuntimeException.class,
            // 设置尝试次数,默认为 3
            // maxAttemptsExpression = "${retry.maxAttempts}",
            maxAttempts = 2,
            // 设置每次尝试间隔时间,默认 1 秒,此处设为 500 毫秒
            // backoff = @Backoff(delayExpression = "${retry.maxDelay}")
            backoff = @Backoff(delay = 500)
    )
    public void call(boolean failed) {
        if (failed) {
            System.out.println("================ >>> retry.");
            throw new RuntimeException();
        }
    }
}

3. 恢复事件

既然有重试处理相对应的也存在恢复事件,当 @Retryable 方法达到重试次数则会触发 @Recover 声明的逻辑。

@Recover 事件匹配逻辑与方法重载类似,其修饰的方法第一个参数必须与 @Retryable 中的 value 类型一致,而其余参数则必须与 @Retryable 修饰的方法入参保持一致。

如上述在 @Retryable 中声明的异常类型为 RuntimeException 且方法入参为单入参 boolean,则其对应的 @Recover 声明如下:

@Component
public class RetryService {

    /**
     * "@Retryable" 重试指定次数后若仍失败则触发 "@Recover"
     */
    @Recover
    public void recover(RuntimeException e, boolean failed) {
        System.out.println("================ >>> recover, params: " + failed);
    }
}

新建测试类 RetryController 调用接口后即可看到控制台分别打印了两句 >>> retry 与一句 >>> recover 日志信息,说明重试机制生效了。

@RestController
@RequestMapping("/api/retry")
public class RetryController {

    @Autowired
    private RetryService retryService;

    @GetMapping("demo1")
    public void demo(boolean failed) {
        retryService.call(failed);
    }
}

4. 配置管理

在之前的方式中都是 @Retryable 注解显式设置具体的尝试策略,而 Spring 也提供了全局的配置修改默认的尝试次数与间隔时间等信息,定义 bean 后则可全局生效替换默认预设值。

具体的配置内容参考下述示例,此处所配置的将全局生效,当然仍可在方法处通过 @Retryable 注解覆盖。

@EnableRetry
@Configuration
public class AppConfig {

    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        // Set retry policy
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(1);
        retryTemplate.setRetryPolicy(retryPolicy);
        // Set interval policy
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(2000l);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        return retryTemplate;
    }
}

文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录