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;
}
}