在实际开发中, 例如在系统请求其他系统或资源的外部调用、操作时,由于网络故障等问题会造成短时间内失败。 我们希望当操作失败时,将使用重试策略来重试该操作。
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.1.5.RELEASE</version> </dependency> 复制代码
<?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> 复制代码
在启动类中添加 @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!!!!") } } 复制代码
在需要重试的方法上添加 @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次,延迟一秒钟。
如果所有重试结果均失败,那么最后抛出的异常将没有任何错误处理逻辑,我们可以使用注解 @Recover
来解决这个问题,例如:
@Recover public boolean retryMethod(RuntimeException ex, String requestId) { log.error("Recovering request {} - {}", requestId,ex.getMessage()); return false; } 复制代码
注意
:方法名、返回值类型、参数值必须一致才生效。
application.properties
添加以下内容
logging.level.org.springframework.retry.support=debug 复制代码
@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); } } 复制代码
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 复制代码
@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; } } 复制代码
application.properties
添加以下内容
retry.template.enable=true retry.template.backOff.period:300 retry.template.max.attempts:5 复制代码
@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; }); } } 复制代码
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 复制代码