调用第三方系统接口,因第三方接口不稳定,偶尔出现调用失败的情况,下一次调用又可能恢复正常,为了避免这种情况,我们需要用到重试机制,多次重试,尽可能避免程序结果出错 ...
微服务feign调用时,经常出现超时情况 ,导致调用失败,实际上不是服务问题,可能是某个时刻请求太多,线程池资源不够,在排队 ... 这里也可以用上 多次重试 , 避免出错...
都是在 while 循环中,进行 try-catch ,若出现异常,则 间隔一定的时间,再次进行重试,直到程序执行获取到正确的结果。
@Slf4j
public class SimpleTryTest {
@SneakyThrows
public static void main(String[] args) {
int tryCount = 3 ; // 重试次数
int num = 0 ; // 次数记录
int n = 0 ; // 模拟异常数
long intervalTime = 1000L ; // 间隔时间
Exception ex = null;
while (tryCount > num){
try {
if(n >= 3){
// if(n >= 2){
System.out.println("n = "+n + " \t 程序执行完毕~");
return;
}
n++;
throw new RuntimeException("异常了==>>> n ="+n);
} catch (Exception e) {
ex= e;
num++;
final String format = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
log.error(format+" ===> 重试,第{}次",num);
}
TimeUnit.MILLISECONDS.sleep(intervalTime);
}
throw new RuntimeException("重试失败,程序执行结束 ", ex);
}
}
21:56:47.015 [main] ERROR com.runcode.springboottourist.retry.test.SimpleTryTest - 2023-03-19 21:56:46 ===> 重试,第1次
21:56:48.038 [main] ERROR com.runcode.springboottourist.retry.test.SimpleTryTest - 2023-03-19 21:56:48 ===> 重试,第2次
21:56:49.049 [main] ERROR com.runcode.springboottourist.retry.test.SimpleTryTest - 2023-03-19 21:56:49 ===> 重试,第3次
Exception in thread "main" java.lang.RuntimeException: 重试失败,程序执行结束
at com.runcode.springboottourist.retry.test.SimpleTryTest.main(SimpleTryTest.java:43)
Caused by: java.lang.RuntimeException: 异常了==>>> n =3
at com.runcode.springboottourist.retry.test.SimpleTryTest.main(SimpleTryTest.java:34)
21:57:50.733 [main] ERROR com.runcode.springboottourist.retry.test.SimpleTryTest - 2023-03-19 21:57:50 ===> 重试,第1次
21:57:51.760 [main] ERROR com.runcode.springboottourist.retry.test.SimpleTryTest - 2023-03-19 21:57:51 ===> 重试,第2次
n = 2 程序执行完毕~
@Slf4j
public class BaseRetry {
/**
* Description: 通用重试方法 --- 模板
* @param param 方法入参
* @param maxTryCount --- 最大重试次数
* @param intervalTime --- 间隔时间
* @return T
* @version v1.0
* @author wu
* @date 2023/3/19 21:30
*/
public T tryMethod(Object param ,int maxTryCount , long intervalTime){
final AtomicInteger num = new AtomicInteger();
Exception ex ;
do{
try {
return exec(param);
} catch (Exception e) {
e.printStackTrace();
ex = e;
}
num.incrementAndGet();
log.warn("进行第:{} 次,重试中 ...",num.get());
try {
TimeUnit.MILLISECONDS.sleep(intervalTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}while (maxTryCount > num.get());
throw new RuntimeException("重试失败==>",ex);
}
protected T exec(Object param) {
return null;
}
}
static class TryOne extends BaseRetry{
int num = 0 ;
/**
* Description: 有实际返回值的
* @param param
* @return java.lang.Integer
* @version v1.0
* @author wu
* @date 2023/3/17 11:05
*/
@Override
protected Integer exec(Object param) {
num++;
if (num > 3) {
return Integer.valueOf(num);
}
throw new RuntimeException("num=="+num);
}
public static void main(String[] args) {
final TryOne tryOne = new TryOne();
// final Object o = tryOne.tryMethod(null, 3, 1000L);
final Object o = tryOne.tryMethod(null, 4, 1000L);
System.out.println("o =="+ o);
}
}
java.lang.RuntimeException: num==1
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.exec(RetryTest.java:30)
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.exec(RetryTest.java:14)
at com.runcode.springboottourist.retry.BaseRetry.tryMethod(BaseRetry.java:32)
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.main(RetryTest.java:36)
22:01:20.763 [main] WARN com.runcode.springboottourist.retry.BaseRetry - 进行第:1 次,重试中 ...
java.lang.RuntimeException: num==2
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.exec(RetryTest.java:30)
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.exec(RetryTest.java:14)
at com.runcode.springboottourist.retry.BaseRetry.tryMethod(BaseRetry.java:32)
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.main(RetryTest.java:36)
22:01:21.778 [main] WARN com.runcode.springboottourist.retry.BaseRetry - 进行第:2 次,重试中 ...
java.lang.RuntimeException: num==3
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.exec(RetryTest.java:30)
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.exec(RetryTest.java:14)
at com.runcode.springboottourist.retry.BaseRetry.tryMethod(BaseRetry.java:32)
at com.runcode.springboottourist.retry.test.RetryTest$TryOne.main(RetryTest.java:36)
22:01:22.781 [main] WARN com.runcode.springboottourist.retry.BaseRetry - 进行第:3 次,重试中 ...
o ==4
static class TryTwo extends BaseRetry{
int num = 0 ;
/**
* Description: 没有实际返回值 --- java.lang.Void
* @param param
* @return java.lang.Void
* @version v1.0
* @author wu
* @date 2023/3/17 11:09
*/
@Override
protected Void exec(Object param) {
num++;
if (num > 3) {
System.out.println("满足条件执行 == num="+num);
return null;
}
throw new RuntimeException("num=="+num);
}
public static void main(String[] args) {
final TryTwo tryTwo = new TryTwo();
// final Object o = tryTwo.tryMethod(null, 3, 1000L);
final Object o = tryTwo.tryMethod(null, 4, 1000L);
System.out.println("o =="+ o);
}
}
java.lang.RuntimeException: num==1
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.exec(RetryTest.java:59)
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.exec(RetryTest.java:41)
at com.runcode.springboottourist.retry.BaseRetry.tryMethod(BaseRetry.java:32)
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.main(RetryTest.java:65)
22:02:13.988 [main] WARN com.runcode.springboottourist.retry.BaseRetry - 进行第:1 次,重试中 ...
java.lang.RuntimeException: num==2
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.exec(RetryTest.java:59)
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.exec(RetryTest.java:41)
at com.runcode.springboottourist.retry.BaseRetry.tryMethod(BaseRetry.java:32)
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.main(RetryTest.java:65)
22:02:14.993 [main] WARN com.runcode.springboottourist.retry.BaseRetry - 进行第:2 次,重试中 ...
java.lang.RuntimeException: num==3
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.exec(RetryTest.java:59)
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.exec(RetryTest.java:41)
at com.runcode.springboottourist.retry.BaseRetry.tryMethod(BaseRetry.java:32)
at com.runcode.springboottourist.retry.test.RetryTest$TryTwo.main(RetryTest.java:65)
22:02:15.999 [main] WARN com.runcode.springboottourist.retry.BaseRetry - 进行第:3 次,重试中 ...
满足条件执行 == num=4
o ==null
看了上面2个案例,每次都需要继承类,重写方法,还要进行一堆配置,太麻烦,有没有更简单的办法的呢. 答案是:有的! Spring 已经准备好了, Spring Retry 组件,程序执行遇到异常时的重试,下面开始使用。
org.springframework.boot
spring-boot-starter-aop
org.springframework.retry
spring-retry
1.3.4
@EnableRetry
@SpringBootApplication
public class SpringBootTouristApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTouristApplication.class, args);
}
}
private int num = 0 ;
@Retryable(value = Exception.class,maxAttempts = 3 ,backoff = @Backoff(delay = 2000,multiplier = 1.5))
public Integer get(int n) {
num++;
if (num >= 3) {
return Integer.valueOf(num);
}
final String date = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
throw new RuntimeException(date +" ===> num=="+num);
}
public Integer getVal() {
return num;
}
public void set(int n){
this.num = n;
}
/**
* Description: 重试失败的回调方法
* @param e 失败的异常
* @param n --- 方法的入参
* @return java.lang.Integer
* @version v1.0
* @author wu
* @date 2023/3/19 21:17
*/
@Recover
public Integer recover(Exception e,int n){
log.error("retry 重试失败,入参是 n={}",n);
log.error("retry failure , cause by {}", ExceptionUtils.getStackTrace(e));
return 1234;
}
@RequestMapping("/retry")
@RestController
public class RetryController {
@Autowired
private RetryService retryService;
@RequestMapping("/get")
public String get(){
final Integer s = retryService.get(5200);
return "获取到的返回值是: s="+s;
}
@RequestMapping(value = "/set")
public String set(Integer n){
if(Objects.isNull(n)){
n=0;
}
retryService.set(n);
final Integer val = retryService.getVal();
return "set成功, num val ="+val ;
}
}
1、 调用:http://localhost:8080/retry/set?n=-0
2、 调用: http://localhost:8080/retry/get
--- 返回值是:
获取到的返回值是: s=3
1、 调用:http://localhost:8080/retry/set?n=-2
2、 调用: http://localhost:8080/retry/get
--- 返回值是:
获取到的返回值是: s=1234
--- 控制台异常输出:
2023-03-19 22:28:38.107 ERROR 67104 --- [nio-8080-exec-7] c.r.s.service.RetryService : retry 重试失败,入参是 n=5200
2023-03-19 22:28:38.110 ERROR 67104 --- [nio-8080-exec-7] c.r.s.service.RetryService : retry failure , cause by java.lang.RuntimeException: 2023-03-19 22:28:38 ===> num==1
at com.runcode.springboottourist.service.RetryService.get(RetryService.java:32)
... ingore
1、spring-retry,最新的版本是:2.0.0 ,该版本要求JDK版本是:17 , 还是Java8,请使用1.3.x 版本的。
启动异常,如下:
2、@Retryable 各个参数的作用,请参考 官方文档,或者亲自试一试,去 org.springframework.retry.annotation.Retryable 里面查看。
3、Spring Retry 实现原理,参考:Spring Retry 的使用和原理_sgvshy的博客-CSDN博客