1. 基本介绍
在业务常见开发中经常我们会涉及到失败重试的逻辑,如当发送通知失败后再次尝试。
针对此类场景最简单粗暴的方式即 CV
大法,但显然这种方式不够优雅,为此 Spring Boot
中提供了 Retry
从而实现优雅的异常失败重试。
2. 依赖导入
在开始前在 Spring Boot
工程中引入相应的 Retry
依赖。
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
3. 异常处理
Spring Retry
的使用也及其简单,通过 @Retryable
注解配置,其基本配置参考下表。
参数 | 描述 |
---|---|
value | 配置何种情况重试。 |
include | 配置触发重试情况的异常类型。 |
exclude | 配置不触发重试情况的异常类型。 |
maxAttempts | 通过配置尝试次数,默认为 3。 |
maxAttemptsExpression | 通过表达式配置尝试次数,默认为 3。 |
backoff | 通过表达式配置间隔时间。 |
recover | 指定重试后仍失败触发的方法。 |
如下述示例代码中则设置当 call()
方法抛出 RuntimeException
异常时重试两次,每次重试间隔 500
毫秒。
@Component
public class RetryService {
@Retryable(
value = RuntimeException.class,
// 设置尝试次数,默认为 3
maxAttempts = 2,
// maxAttemptsExpression = "${retry.maxAttempts}",
// 设置每次尝试间隔时间,默认 1 秒,此处设为 500 毫秒
backoff = @Backoff(delay = 500)
// 通过表达式配置间隔时间
// backoff = @Backoff(delayExpression = "${retry.maxDelay}")
)
public void call(boolean failed) {
if (failed) {
System.out.println("================ >>> retry.");
throw new RuntimeException();
}
}
}
4. 恢复事件
重试的恢复事件由 @Recover
注解控制,即当 @Retryable
方法达到重试次数仍失败时触发。
需要注意的是 @Recover
注解的标注的方法第一个参数必须与 @Retryable
中的 value
类型一致,剩余参数则必须与 @Retryable
修饰的方法入参一致。
@Component
public class RetryService {
/**
* "@Retryable" 重试指定次数后若仍失败则触发 "@Recover"
* <p>
* 第一个方法参数为 "@Retryable" 中监听的异常类型
* 第二个方法参数为 "@Retryable" 方法中剩余的入参
*/
@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);
}
}
5. 配置管理
除了在方法上通过 @Retryable
注解显式设置尝试策略,也可通过全局的配置修改默认的尝试次数与间隔时间等信息。
新建配置类 AppConfig
,具体的配置内容参考下述示例,此处的配置将全局生效,当然仍可在方法处通过 @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;
}
}