Spring优雅重试@EnableRetry
如果不用spring的写法
// 简单递归+次数限制+线程睡眠
package com.example.springexample.springretry;
/**
* @desc: RetryDemo
* @author: pmdream
* @date: 2023/3/15 1:51 AM
*/
public class RetryDemo {
public static void retry(int i) {
if (i <= 3) {
i++;
try {
if (method()) {
System.out.println("成功");
} else {
Thread.sleep(1000);
System.out.println("重试:" + i + "次");
retry(i);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static boolean method() {
System.out.println("do some thing");
return false;
}
public static void main(String[] args) {
retry(0);
}
}
结果:
do some thing
重试:1次
do some thing
重试:2次
do some thing
重试:3次
do some thing
重试:4次
使用spring的优雅写法
引入 maven 依赖:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
如果不增加切面maven依赖,会报错:
Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: org/aspectj/lang/annotation/Pointcut
启动类需要加入:
@EnableRetry
package com.example.springexample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
@SpringBootApplication
@EnableRetry
public class SpringExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringExampleApplication.class, args);
}
}
测试方法:
package com.example.springexample.springretry;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class SpringRetryServiceImplTest {
@Autowired
ISpringRetryService springRetryService;
@Test
public void method() {
springRetryService.method();
}
}
package com.example.springexample.springretry;
/**
* @desc: ISpringRetryService
* @author: pmdream
* @date: 2023/3/15 1:29 AM
*/
public interface ISpringRetryService {
void method();
}
package com.example.springexample.springretry;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
/**
* @desc: SpringRetryServiceImpl
* @author: pmdream
* @date: 2023/3/15 1:27 AM
*/
@Service
public class SpringRetryServiceImpl implements ISpringRetryService {
@Retryable(value = {RuntimeException.class}, maxAttempts = 5, backoff = @Backoff(delay = 5000L))
@Override
public void method() {
System.out.println(System.currentTimeMillis() + "method exec");
doSomeThing();
}
private void doSomeThing() {
// do some thing
throw new RuntimeException("throw exception");
}
}
测试结果:
可以看到,时间是5000毫秒一次
1678815994214method exec
1678815999218method exec
1678816004221method exec
1678816009223method exec
@Retryable注解中的参数说明:
maxAttempts :最大重试次数,默认为3,如果要设置的重试次数为3,可以不写;
value:抛出指定异常才会重试
include:和value一样,默认为空,当exclude也为空时,默认所有异常
exclude:指定不处理的异常
backoff:重试等待策略,默认使用@Backoff的value默认为1000L
@Backoff注解中的参数说明:
delay:每次重试延迟毫秒数,默认为0L
value:delay的别名,默认为1000L,当delay>0时,value将会被忽略
maxDelay:最大延迟毫秒数,默认为0L,
multiplier:(指定延迟倍数)默认为0;大于0时生效;如果delay等于2,multiplier等于2,则第一次重试为2秒,第二次为4秒,第三次为8秒…
random:随机值加权,默认为false;当multiplier>0时,上次延迟毫秒 < 延迟时间 < 最大延迟 * multiplier