一、Spring-Retry
Spring-Retry
框架是Spring
自带的功能,具备间隔重试、包含异常、排除异常、控制重试频率等特点,是项目开发中很实用的一种框架。
支持手动调用方式和注解方式。
使用需引入下面依赖:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
在 Spring-Retry
中主要有 重试策略 和 重试回退策略 两个重要的点。
其中 重试策略 支持:
- NeverRetryPolicy: 只允许调用
RetryCallback
一次,不允许重试。 - AlwaysRetryPolicy: 允许无限重试,直到成功,此方式逻辑不当会导致死循环。
- SimpleRetryPolicy: 固定次数重试策略,默认重试最大次数为
3
,也是默认使用的策略。 - TimeoutRetryPolicy: 超时时间重试策略,默认超时时间为
1
秒,在指定的超时时间内允许重试,例如设置5s
, 则在5s
内可以一直重试。 - ExceptionClassifierRetryPolicy: 设置不同异常的重试策略,类似组合重试策略,区别在于这里只区分不同异常的重试。
- CircuitBreakerRetryPolicy: 有熔断功能的重试策略,需设置
3
个参数openTimeout
、resetTimeout
和delegate
。 - CompositeRetryPolicy: 组合重试策略,有两种组合方式,乐观组合重试策略是指只要有一个策略允许即可以重试,悲观组合重试策略是指只要有一个策略不允许即可以重试,但不管哪种组合方式,组合中的每一个策略都会执行。
重试回退策略,指的是每次需要重试是立即重试还是等待一段时间重试,默认情况下是立即重试,支持如下策略:
- NoBackOffPolicy: 无退避算法策略,每次重试时立即重试。
- FixedBackOffPolicy: 固定时间的退避策略,需设置参数
sleeper
和backOffPeriod
,sleeper
指定等待策略,默认是Thread.sleep
,即线程休眠,backOffPeriod
指定休眠时间,默认1
秒。 - UniformRandomBackOffPolicy: 随机时间退避策略,需设置
sleeper
、minBackOffPeriod
和maxBackOffPeriod
,该策略在minBackOffPeriod
,maxBackOffPeriod
之间取一个随机休眠时间,minBackOffPeriod
默认500
毫秒,maxBackOffPeriod
默认1500
毫秒。 - ExponentialBackOffPolicy: 指数退避策略,需设置参数
sleeper
、initialInterval
、maxInterval
和multiplier
,initialInterval
指定初始休眠时间,默认100
毫秒,maxInterval
指定最大休眠时间,默认30
秒,multiplier
指定乘数,即下一次休眠时间为当前休眠时间*multiplier
。 - ExponentialRandomBackOffPolicy: 随机指数退避策略,引入随机乘数可以实现随机乘数回退。
二、手动方式使用
声明 RetryTemplate
,这里我已 SimpleRetryPolicy
和 TimeoutRetryPolicy
两个策略进行演示:
@Configuration
public class RetryConfig {
@Bean(name = "simpleRetry")
public RetryTemplate simpleRetryPolicy() {
// 声明重试模版
RetryTemplate retryTemplate = new RetryTemplate();
// 需要重试的异常
Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();
exceptionMap.put(RuntimeException.class, true);
// 指定重试次数和重试异常
SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy(5, exceptionMap);
// 指定重试策略
retryTemplate.setRetryPolicy(simpleRetryPolicy);
// 声明重试回退操作策略,
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
// 重试间隔时间
fixedBackOffPolicy.setBackOffPeriod(1000);
// 指定回退策略
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
return retryTemplate;
}
@Bean(name = "timeoutRetry")
public RetryTemplate timeoutRetry() {
// 声明重试模版
RetryTemplate retryTemplate = new RetryTemplate();
// 超时时间重试策略,默认超时时间为1秒,在指定的超时时间内允许重试
TimeoutRetryPolicy timeoutRetryPolicy = new TimeoutRetryPolicy();
timeoutRetryPolicy.setTimeout(5000);
// 指定重试策略
retryTemplate.setRetryPolicy(timeoutRetryPolicy);
// 声明重试回退操作策略,
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
// 重试间隔时间
fixedBackOffPolicy.setBackOffPeriod(1000);
// 指定回退策略
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
return retryTemplate;
}
}
测试接口中使用:
@Slf4j
@RestController
@RequestMapping("/test1")
public class TestController1 {
@Resource(name = "simpleRetry")
RetryTemplate simpleRetry;
@Resource(name = "timeoutRetry")
RetryTemplate timeoutRetry;
@GetMapping("/t1")
public String t1() {
return simpleRetry.execute(context -> {
log.info("开始执行!");
throw new RuntimeException("大于2,--异常");
}, context -> {
log.info("已达到最大重试次数!");
return "fail";
});
}
@GetMapping("/t2")
public String t2() throws InterruptedException {
return timeoutRetry.execute(context -> {
log.info("开始执行!");
// 模拟耗时
Thread.sleep(1000);
throw new RuntimeException("异常!");
}, context -> {
log.info("已达到最大重试次数!");
return "fail";
});
}
}
测试 /test1/t1
接口:
测试 /test1/t2
接口:
三、注解方式
首先需要在启动类上增加注解:
@EnableRetry
通过在方法上增加 @Retryable
注解实现重试的效果,通过 @Recover
执行最大重试次数的降级处理:
例如:
@Slf4j
@RestController
@RequestMapping("/test2")
public class TestController2 {
@Retryable(
value = {RuntimeException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 2000L, multiplier = 2)
)
@GetMapping("/t1")
public String t1(String param) {
log.info("接收参数:{}", param);
int i = RandomUtils.nextInt(0, 11);
log.info("随机生成的数:{}", i);
if (i > 2) {
throw new RuntimeException("大于2,--异常");
}
return "success";
}
/**
* 达到最大重试次数
*/
@Recover
public String recover(RuntimeException e, String param) {
log.info("达到最大重试次数! 接收参数:{}", param);
return "fail";
}
}