程序调用第三方接口可能会出现网络抖动、超时等异常情况,这时我们通常会想到当是重试。我们首先模拟一段业务逻辑,然后开始我们重试代码当编写
/**
* 这个是需要执行的业务逻辑
* 定义了一个随机数,当低于阈值的时候,抛出异常
* 调用方catch住异常后进行重试
*/
private void doSomething() {
log.info("开始业务逻辑...");
int random = RandomUtils.nextInt(100);
log.info("随机数为:{}", random);
if (random < 90) {
log.info("随机数低于阈值,准备重试");
throw new ServiceException("业务异常");
}
log.info("结束业务逻辑...");
}
复制代码
1. 普通重试
最普通的重试逻辑,就是在调用方捕获到异常后,再次调用业务逻辑方法(递归),直到成功。该方案简单粗暴
public void normal(int count) {
if (count < 5) {
count += 1;
try {
doSomething();
} catch (Exception e) {
//此处可以定时休眠一点时间再次重试
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
//重试
normal(count);
}
}
}
复制代码
2. SpringRetry 普通模式
Spring-retry是一个开源工具包,该工具把重试操作模板定制化,可以设置重试策略和回退策略。同时重试执行实例保证线程安全,下面给出对应的示例代码
/**
* Spring提供的Retry机制
*/
public void springRetry() {
// 构建重试模板实例
RetryTemplate retryTemplate = new RetryTemplate();
// 设置重试策略,主要设置重试次数和需要捕获的异常
SimpleRetryPolicy policy = new SimpleRetryPolicy(5, Collections.singletonMap(Exception.class, true));
// 设置重试回退操作策略,主要设置重试间隔时间
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
//初始间隔
backOffPolicy.setInitialInterval(1000);
//最大间隔
backOffPolicy.setMaxInterval(10 * 1000L);
//递增倍数(即下次间隔是上次的多少倍)
backOffPolicy.setMultiplier(2);
retryTemplate.setRetryPolicy(policy);
retryTemplate.setBackOffPolicy(backOffPolicy);
// 通过RetryCallback 重试回调实例包装正常逻辑逻辑,第一次执行和重试执行执行的都是这段逻辑
final RetryCallback
3. SpringRetry 注解版本
在上面的代码基础上,Spring提供了重试操作的注解,下面给出对应的示例代码。使用该方式的时候需要注意以下两点:
(1) 需要在入口类上添加@EnableRetry
(2) @Retryable只能出现在最外层的方法,同一个类中,当某个方法调用加类该注解的方法时,重试不生效
/**
* Spring提供的Retry机制
*/
@Retryable(value = ServiceException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
public void springRetryAnnotation() {
doSomething();
}
/**
* 重试失败后调用的方法(注意,需跟重处理方法在同一个类中)
*/
@Recover
public void recover(ServiceException e) {
log.info("执行重试结束依然失败后的代码");
}
复制代码
4. GuavaRetry版本
SpringRetry版本只能对异常进行重试,对于自定义对数据结构不能支持,如果有这方面需求的化,可以考虑用GuavaRetry进行重试,具体示例代码如下(SpringTime默认不集成,使用时需要手动添加maven依赖)
/**
* guava提供的retry机制
*/
public void guavaRetry() {
//设置重试5次,同样可以设置重试超时时间
StopStrategy stopStrategy = StopStrategies.stopAfterAttempt(5);
//设置每次重试间隔
WaitStrategy waitStrategy = WaitStrategies.exponentialWait(2, 10, TimeUnit.SECONDS);
Retryer retryer = RetryerBuilder.newBuilder().retryIfException().withStopStrategy(stopStrategy)
.withWaitStrategy(waitStrategy).build();
try {
retryer.call(() -> {
doSomething();
return null;
});
} catch (ExecutionException | RetryException e) {
e.printStackTrace();
}
}
复制代码