1 背景
在实际开发中, 例如在系统请求其他系统或资源的外部调用、操作时,由于网络故障等问题会造成短时间内失败。 我们希望当操作失败时,将使用重试策略来重试该操作。
2 添加依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.5.RELEASE</version>
</dependency>
复制代码
3 完整依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.devin</groupId>
<artifactId>java-tutorial</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 重试依赖 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<!-- Aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
</project>
复制代码
4 开启重试
在启动类中添加@EnableRetry
注解来启用全局重试。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
@SpringBootApplication
@EnableRetry
@Slf4j
public class SpringRertyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringRertyApplication.class, args);
log.info("Spring Retry start ok!!!!")
}
}
复制代码
5 通过注解@Retryable实现重试
5.1 添加@Retryable注解
在需要重试的方法上添加@Retryable
注解
@Retryable(
maxAttempts = 5,
backoff = @Backoff (delay = 1000L),
value = {
RuntimeException.class
}
)
public boolean retryMethod(String requestId) {
log.info("Processing request: {}", requestId);
throw new RuntimeException("Failed Request");
}
复制代码
示例中,仅当方法抛出RuntimeException
时才尝试重试。 将最多进行5
次重试,并延迟1000
毫秒。 默认@Retryable
不带任何属性,则该方法失败并发生异常,则重试最多3次,延迟一秒钟。
5.2 添加@Recover注解
如果所有重试结果均失败,那么最后抛出的异常将没有任何错误处理逻辑,我们可以使用注解@Recover
来解决这个问题,例如:
@Recover
public boolean retryMethod(RuntimeException ex, String requestId) {
log.error("Recovering request {} - {}", requestId,ex.getMessage());
return false;
}
复制代码
注意
:方法名、返回值类型、参数值必须一致才生效。
5.3 配置打印重试日志
application.properties
添加以下内容
logging.level.org.springframework.retry.support=debug
复制代码
5.4 测试
@Slf4j
@Component
public class RetryAnnotationTest implements ApplicationRunner {
@Resource
private RetryAnnotationService retryAnnotationService;
@Override
public void run(ApplicationArguments args) throws Exception {
boolean result = retryAnnotationService.retryMethod(RandomUtil.randomNumbers(10));
log.info("result = {}",result);
}
}
复制代码
5.5 执行结果
2020-03-25 14:50:59.099 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Retry: count=0
2020-03-25 14:50:59.102 INFO [aop-spel,,,] 11212 --- [ main] c.s.s.r.a.RetryAnnotationService : Processing request: 4521824259
2020-03-25 14:51:00.104 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Checking for rethrow: count=1
2020-03-25 14:51:00.104 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Retry: count=1
2020-03-25 14:51:00.104 INFO [aop-spel,,,] 11212 --- [ main] c.s.s.r.a.RetryAnnotationService : Processing request: 4521824259
2020-03-25 14:51:01.104 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Checking for rethrow: count=2
2020-03-25 14:51:01.104 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Retry: count=2
2020-03-25 14:51:01.104 INFO [aop-spel,,,] 11212 --- [ main] c.s.s.r.a.RetryAnnotationService : Processing request: 4521824259
2020-03-25 14:51:02.106 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Checking for rethrow: count=3
2020-03-25 14:51:02.106 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Retry: count=3
2020-03-25 14:51:02.106 INFO [aop-spel,,,] 11212 --- [ main] c.s.s.r.a.RetryAnnotationService : Processing request: 4521824259
2020-03-25 14:51:03.106 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Checking for rethrow: count=4
2020-03-25 14:51:03.106 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Retry: count=4
2020-03-25 14:51:03.106 INFO [aop-spel,,,] 11212 --- [ main] c.s.s.r.a.RetryAnnotationService : Processing request: 4521824259
2020-03-25 14:51:03.106 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Checking for rethrow: count=5
2020-03-25 14:51:03.107 DEBUG [aop-spel,,,] 11212 --- [ main] o.s.retry.support.RetryTemplate : Retry failed last attempt: count=5
2020-03-25 14:51:03.107 ERROR [aop-spel,,,] 11212 --- [ main] c.s.s.r.a.RetryAnnotationService : Recovering request 4521824259 - Failed Request
复制代码
6 通过RetryTemplate实现重试
6.1 创建RetryTemplate配置
@Configuration
@ConditionalOnProperty(name = "retry.template.enable", havingValue = "true")
public class RetryConfig {
/**
* 多少毫秒以后重试
*/
@Value("${retry.template.backOff.period:5000}")
private Long backOffPeriod;
/**
* 重试次数
*/
@Value("${retry.template.max.attempts:3}")
private Integer maxAttempts;
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
//定义重试时间
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(backOffPeriod);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
//定义重试次数
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(maxAttempts);
retryTemplate.setRetryPolicy(retryPolicy);
return retryTemplate;
}
}
复制代码
6.2 重试配置
application.properties
添加以下内容
retry.template.enable=true
retry.template.backOff.period:300
retry.template.max.attempts:5
复制代码
6.3 使用RetryTemplate
@Slf4j
@Component
public class RetryTemplateTest implements ApplicationRunner {
@Resource
private RetryTemplate retryTemplate;
@Override
public void run(ApplicationArguments args) {
try {
boolean result = retryMethod(RandomUtil.randomNumbers(10));
log.info("Request Result={}", result);
} catch (RequestRetryException e) {
log.error("Request Exception - message:{}", e.getMessage());
}
}
private boolean retryMethod(String requestId) throws RequestRetryException {
return retryTemplate.execute(context -> {
log.info("Processing request - Param={} - Retry: count={}", requestId, context.getRetryCount());
//TODO 业务逻辑处理
throw new RequestRetryException("Request Retry");
}, context -> {
log.info("Recovering request - Param={} - Retry: count={}", requestId, context.getRetryCount());
//TODO 错误逻辑处理
return false;
});
}
}
复制代码
6.4 执行结果
2020-03-25 15:19:24.112 INFO [aop-spel,,,] 4120 --- [ main] c.s.s.r.template.RetryTemplateTest : Processing request - Param=5581499424 - Retry: count=0
2020-03-25 15:19:24.414 INFO [aop-spel,,,] 4120 --- [ main] c.s.s.r.template.RetryTemplateTest : Processing request - Param=5581499424 - Retry: count=1
2020-03-25 15:19:24.715 INFO [aop-spel,,,] 4120 --- [ main] c.s.s.r.template.RetryTemplateTest : Processing request - Param=5581499424 - Retry: count=2
2020-03-25 15:19:25.015 INFO [aop-spel,,,] 4120 --- [ main] c.s.s.r.template.RetryTemplateTest : Processing request - Param=5581499424 - Retry: count=3
2020-03-25 15:19:25.315 INFO [aop-spel,,,] 4120 --- [ main] c.s.s.r.template.RetryTemplateTest : Processing request - Param=5581499424 - Retry: count=4
2020-03-25 15:19:25.316 INFO [aop-spel,,,] 4120 --- [ main] c.s.s.r.template.RetryTemplateTest : Recovering request - Param=5581499424 - Retry: count=5
2020-03-25 15:19:25.316 INFO [aop-spel,,,] 4120 --- [ main] c.s.s.r.template.RetryTemplateTest : Request Result=false
复制代码
总结
在本文中,我们通过使用注解和RetryTemplate的示例介绍了Spring Retry。