SpringBoot 下使用 @Retryable 自动重试注解
source link: https://maxqiu.com/article/detail/144
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
SpringBoot 下使用 @Retryable 自动重试注解
2022/10/08 Java SpringBoot
当系统中调用一些第三方服务时(如使用 http
请求),如果第三方服务不是很稳定(比如网络波动),可以使用 SpringBoot
的自动重试功能
以下代码以 SpringBoot 2.7.3
为例
在 pom.xml
中引入如下依赖
<!-- 引入重试 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<!-- 额外添加aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
必须要引入 aspectj
,否则会显示 NoClassDefFoundError
异常
Application
启动类
在启动类中,需要添加 @EnableRetry
注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
// 启用重试
@EnableRetry
@SpringBootApplication
public class RetryApplication {
public static void main(String[] args) {
SpringApplication.run(RetryApplication.class, args);
}
}
service
服务类
主要使用以下三个注解,具体配置项见代码注解
@Retryable
:指定哪个方法执行重试@Backoff
:延迟配置@Recover
:最终回调处理
import java.net.http.HttpConnectTimeoutException;
import java.time.LocalTime;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@Service
@Slf4j
public class RetryService {
/**
* 需要执行重试的方法
*/
@Retryable(
// include:同value,当执行重试的异常类型(可以多个)
// include = Exception.class,
// exclude:要排除的异常类型(可以多个)
// exclude = {},
// 当 include 和 exclude 均为空时,所有异常均重试
// maxAttempts:最大重试次数(包括第一次失败)
maxAttempts = 5,
// 重试配置
backoff = @Backoff(
// 延迟时间,单位:毫秒
delay = 1000,
// 最大延迟时间(默认值为0即不启用,若小于delay值则为3000),单位:毫秒
maxDelay = 3000,
// 相对上一次延迟时间的倍数(比如2:第一次1000毫秒,第二次2000毫秒,第三次4000毫秒...)
multiplier = 2))
public int retry(Integer code, String name) throws Exception {
log.info("retryTest被调用,时间:{}", LocalTime.now());
if (code == 1) {
// 此处随意使用了一个检查异常
throw new HttpConnectTimeoutException("抛出自定义异常信息!");
}
// 这里可能会产生非检查异常
int i = 2 / code;
log.info("retryTest调用成功!");
return i;
}
/**
* 使用 @Recover 注解做最终失败处理(可以针对不同的异常定义多个最终失败处理)<br>
* 第一个参数:需要处理的异常类型<br>
* 后面的参数:(可选)与重试方法相同顺序和类型的参数<br>
* 返回值类型:必须与重试方法的类型相同
*/
@Recover
public int recover(HttpConnectTimeoutException e, Integer code, String name) {
log.error("HttpConnectTimeoutException回调方法执行!");
log.error("异常信息:{}", e.getMessage());
log.error("参数code:{}", code);
log.error("参数name:{}", name);
return 1;
}
/**
* 建议定义一个所有 Exception 异常处理,用于处理非检查异常
*/
@Recover
public int recover(Exception e) {
log.error("Exception回调方法执行!");
log.error("异常信息:{}", e.getMessage());
return 1;
}
}
编写测试类
import javax.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import com.maxqiu.demo.service.RetryService;
import lombok.extern.slf4j.Slf4j;
@SpringBootTest
class RetryServiceTest {
@Resource
private RetryService retryService;
@Test
void retryTest() {
try {
log.info("调用方法,参数:{}", 1);
log.info("返回结果:{}\n", retryService.retry(1, "张三"));
log.info("返回结果:{}\n", retryService.retry(0, "张三"));
log.info("返回结果:{}\n", retryService.retry(2, "张三"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
2022-10-08 22:04:21.976 INFO 14572 --- [ main] com.maxqiu.demo.RetryServiceTest : 调用方法,参数:1
2022-10-08 22:04:22.012 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:22.012843100
2022-10-08 22:04:23.022 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:23.022396600
2022-10-08 22:04:25.027 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:25.027826100
2022-10-08 22:04:29.038 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:29.038549100
2022-10-08 22:04:34.051 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:34.051500400
2022-10-08 22:04:34.051 ERROR 14572 --- [ main] com.maxqiu.demo.service.RetryService : HttpConnectTimeoutException回调方法执行!
2022-10-08 22:04:34.052 ERROR 14572 --- [ main] com.maxqiu.demo.service.RetryService : 异常信息:抛出自定义异常信息!
2022-10-08 22:04:34.052 ERROR 14572 --- [ main] com.maxqiu.demo.service.RetryService : 参数code:1
2022-10-08 22:04:34.052 ERROR 14572 --- [ main] com.maxqiu.demo.service.RetryService : 参数name:张三
2022-10-08 22:04:34.052 INFO 14572 --- [ main] com.maxqiu.demo.RetryServiceTest : 返回结果:指定异常处理
2022-10-08 22:04:34.052 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:34.052502
2022-10-08 22:04:35.053 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:35.053312200
2022-10-08 22:04:37.068 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:37.068952200
2022-10-08 22:04:41.069 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:41.069102200
2022-10-08 22:04:46.080 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:46.080276100
2022-10-08 22:04:46.081 ERROR 14572 --- [ main] com.maxqiu.demo.service.RetryService : Exception回调方法执行!
2022-10-08 22:04:46.081 ERROR 14572 --- [ main] com.maxqiu.demo.service.RetryService : 异常信息:/ by zero
2022-10-08 22:04:46.081 INFO 14572 --- [ main] com.maxqiu.demo.RetryServiceTest : 返回结果:所有异常处理
2022-10-08 22:04:46.081 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:04:46.081032900
2022-10-08 22:04:46.081 INFO 14572 --- [ main] com.maxqiu.demo.service.RetryService : 方法调用成功!,结果:1
2022-10-08 22:04:46.082 INFO 14572 --- [ main] com.maxqiu.demo.RetryServiceTest : 返回结果:SUCCESS
- 在发生异常时,方法被调用了 5 次,说明重试生效了
- 方法重试的时间间隔逐渐增大且不超过 5s ,说明延迟配置生效了
- 指定的检查异常和非检查异常都可以捕获,说明回调生效了
@Retryable
修饰的方法被同一个类的其他普通方法调用时不会生效,比如下文中调用 callInSameClass
会直接抛出异常:
@Service
@Slf4j
public class RetryService {
/**
* 如果在同一个类中调用重试方法,重试注解不会生效
*/
public String callInSameClass(int code, String name) throws Exception {
return retry(code, name);
}
/**
* 需要执行重试的方法
*/
@Retryable
public String retry(Integer code, String name) throws Exception {
...
}
}
2022-10-08 22:05:10.956 INFO 15332 --- [ main] com.maxqiu.demo.RetryServiceTest : 调用方法,参数:1
2022-10-08 22:05:10.976 INFO 15332 --- [ main] com.maxqiu.demo.service.RetryService : 方法被调用,时间:22:05:10.976267400
java.net.http.HttpConnectTimeoutException: 抛出自定义异常信息!
正确的调用方式应该是在另一个类中调用重试方法
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class CallService {
@Resource
private RetryService retryService;
/**
* 如果在另一个类中调用重试方法,可以生效
*/
public String callInOtherClass(int code, String name) throws Exception {
return retryService.retry(code, name);
}
}
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK