最近研究openfeign的重试原理时,发现其依赖spring-retry框架,不禁好奇并测试一二。使用步骤如下:
1、添加pom.xml依赖(springboot版本为2.6.14)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.14</version>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<revision>1.3.4</revision>
</dependency>
2、启用重试框架,启动函数上添加注解 @EnableRetry
3、在方法上添加重试注解 @Retryable
@Retryable(maxAttempts = 3, recover = "recoverTest1", listeners = "customRetryListener", backoff = @Backoff(delay = 2000, multiplier = 2))
public Boolean test1(String user) {
log.info("执行操作,参数user={}", user);
if (Objects.equals(user, "张三")) {
throw new RuntimeException("重试测试");
}
return Boolean.TRUE;
}
注解主要参数说明:
- maxAttempts:最大尝试次数(包含第一次)
- recover:重试全部失败后调用回退方法名,回退放假
- listeners:监听类,值为Bean名称
- backoff:重试等待策略
- backoff.delay:延迟基数,默认1000(1秒)
- backoff.multiplier:延迟倍数,默认0(忽略设置),本次延迟=上次延迟*multiplier。如delay=2000&multiplier=3时,第一次重试间隔2秒,第二次重试间隔6秒,第三次重试间隔18秒。参考ExponentialBackOffPolicy.backOff
- backoff.maxDelay:最大延迟时间,默认0(忽略设置)
4、添加回退方法,方法名上增加注解 @Recover,第一个入参为异常信息,其他入参和出参跟操作方法一致
@Recover
private Boolean recoverTest1(RuntimeException e, String user) {
log.warn("操作回退,错误信息:{}。参数user={}", e.getMessage(), user);
return Boolean.FALSE;
}
5、重试监听
@Slf4j
@Component
public class CustomRetryListener implements RetryListener {
/**
* 只在首次调用前执行
*/
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
log.info("尝试已打开,当前重试次数:{}", context.getRetryCount());
return true;
}
/**
* 只在重试次数用完或调用成功后执行
*/
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable e) {
log.info("尝试已关闭,当前尝试次数:{}", context.getRetryCount());
}
/**
* 每次调用发生异常时执行
*/
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable e) {
log.warn("第{}次尝试失败,异常信息:{}", context.getRetryCount(), e.getMessage());
}
}
6、异步方法同样可以重试
@Async
@Retryable(maxAttempts = 2, recover = "recoverTest2", backoff = @Backoff(delay = 1000))
public void test2(String user) {
log.info("执行操作,参数user={}", user);
if (Objects.equals(user, "张三")) {
throw new RuntimeException("重试测试");
}
}
@Recover
private void recoverTest2(RuntimeException e, String user) {
log.warn("操作回退,异常信息:{}。参数user={}", e.getMessage(), user);
}
7、使用RetryTemplate
定义RetryTemplate
@Bean
public RetryTemplate retryTemplate(CustomRetryListener retryListener) {
// 重试策略为ExponentialBackOffPolicy
return RetryTemplate.builder().exponentialBackoff(2000, 2, 10000).withListener(retryListener).build();
}
使用RetryTemplate
@Autowired
private RetryTemplate retryTemplate;
public String test3() {
String result = retryTemplate.execute(new RetryCallback<String, RuntimeException>() {
@Override
public String doWithRetry(RetryContext context) throws RuntimeException {
log.info("当前尝试次数:{}", context.getRetryCount());
throw new RuntimeException("重试测试");
}
}, new RecoveryCallback<String>() {
@Override
public String recover(RetryContext context) throws Exception {
log.info("操作失败,执行回退");
return "error";
}
});
return result;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步