前言
当我们进行接口调用的时候,可能由于各种原因会出现调用失败,比如网络异常。这种情况下需要我们再次调用。通过一些框架可以替我们快速实现这种重试机制。本篇文章就简单的记录一下SpringRetry的基本使用。
幂等性
HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。对于任意多次执行对资源本身所产生的影响均与一次执行的影响相同。如果接口不能保证幂等性支持的话,使用重试机制可能会发生以下非预期的结果。
Spring Retry注解方式
1. 引入Maven依赖
<!-- 重试机制 -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2. Spring配置类添加注解@EnableRetry
@EnableRetry
@Configuration
public class RetryConfiguration {
}
3. 业务代码使用
业务代码中使用主要分为两步:
- 使用@Retryable注解作用于需要重试的代码,并配置重试策略
- 使用@Recover注解作用于重试失败代码逻辑
示例代码:
@Service
public class RetryService {
private Logger logger = LoggerFactory.getLogger(RetryService.class);
// 1. 使用@Retryable注解作用于需要重试的代码
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 2))
public void devide(double a, double b){
logger.info("开始进行除法运算");
if (b == 0) {
throw new RuntimeException();
}
logger.info("{} / {} = {}", a, b, a / b);
}
// 2. 使用@Recover注解作用于重试失败代码逻辑
@Recover
public void recover() {
logger.error("被除数不能为0");
}
}
@Retryable主要参数:
- value: 抛出指定异常进行重试
- include:和value一样,默认为空,当exclude也为空时,默认所有异常
- exclude:指定不处理的异常
- maxAttempts:最大重试次数,默认3次
- backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L;multiplier(指定延迟倍数)
@Recover说明:
当重试达到指定次数时候该注解的方法将被回调
发生的异常类型需要和@Recover注解的参数一致
@Retryable注解的方法不能有返回值,不然@Recover注解的方法无效
4.测试
示例代码:
@RunWith(SpringRunner.class)
@SpringBootTest
public class BootdemoApplicationTests {
@Autowired
private RetryService retryService;
private Logger logger = LoggerFactory.getLogger(BootdemoApplication.class);
@Test
public void retryTest() {
//int count = retryService.retry(-1);
retryService.retry(-1);
//logger.info("库存为:" + count);
}
}
5.重试失效场景
- @Retryable注解作用方法不能在本类使用。示例代码中直接调用execute方法重试机制将不会生效,而调用devide则重试生效。
public void execute(double a, double b) throws DevideException {
devide(a, b);
}
@Retryable(value = DevideException.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 2))
public void devide(double a, double b) throws DevideException {
logger.info("开始进行除法运算");
if (b == 0) {
throw new DevideException("被除数不能为0");
}
logger.info("{} / {} = {}", a, b, a / b);
}
- 使用@Retryable不能使用try catch捕获异常
使用RetryTemplate API
1. 配置类创建RetryTemplate
@Configuration
public class RetryConfig {
@Bean
public RetryTemplate cacheCleanRetryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
// 简单重试策略,固定重试次数
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(3);
retryTemplate.setRetryPolicy(retryPolicy);
// 固定退避策略
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(1000L);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
return retryTemplate;
}
}
2、使用RetryTemplate
RetryTemplate主要执行方法execute,需要我们传入RetryCallback和RecoveryCallback方法实现
@Override
public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback,
RecoveryCallback<T> recoveryCallback) throws E {
return doExecute(retryCallback, recoveryCallback, null);
}
参考文章
http://www.itclj.com/blog/59940a4081c06e672f942ae1
https://www.jb51.net/article/139642.htm
https://blog.csdn.net/xiaolyuh123/article/details/80209815