程序调用第三方接口可能会出现网络抖动、超时等异常状况,这时咱们一般会想到当是重试。咱们首先模拟一段业务逻辑,而后开始咱们重试代码当编写java
/** * 这个是须要执行的业务逻辑 * 定义了一个随机数,当低于阈值的时候,抛出异常 * 调用方catch住异常后进行重试 */
private void doSomething() {
log.info("开始业务逻辑...");
int random = RandomUtils.nextInt(100);
log.info("随机数为:{}", random);
if (random < 90) {
log.info("随机数低于阈值,准备重试");
throw new ServiceException("业务异常");
}
log.info("结束业务逻辑...");
}
复制代码
最普通的重试逻辑,就是在调用方捕获到异常后,再次调用业务逻辑方法(递归),直到成功。该方案简单粗暴spring
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);
}
}
}
复制代码
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<Object, Exception> retryCallback = context -> {
log.info("开始重试,重试次数为: {}", context.getRetryCount());
doSomething();
return null;
};
// 经过RecoveryCallback 重试流程正常结束或者达到重试上限后的退出恢复操做实例
final RecoveryCallback<Object> recoveryCallback = context -> {
log.info("执行重试结束依然失败后的代码");
return null;
};
try {
// 由retryTemplate 执行execute方法开始逻辑执行
retryTemplate.execute(retryCallback, recoveryCallback);
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
在上面的代码基础上,Spring提供了重试操做的注解,下面给出对应的示例代码。使用该方式的时候须要注意如下两点:bash
(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("执行重试结束依然失败后的代码");
}
复制代码
SpringRetry版本只能对异常进行重试,对于自定义对数据结构不能支持,若是有这方面需求的化,能够考虑用GuavaRetry进行重试,具体示例代码以下(SpringTime默认不集成,使用时须要手动添加maven依赖)dom
/** * guava提供的retry机制 */
public void guavaRetry() {
//设置重试5次,一样能够设置重试超时时间
StopStrategy stopStrategy = StopStrategies.stopAfterAttempt(5);
//设置每次重试间隔
WaitStrategy waitStrategy = WaitStrategies.exponentialWait(2, 10, TimeUnit.SECONDS);
Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder().retryIfException().withStopStrategy(stopStrategy)
.withWaitStrategy(waitStrategy).build();
try {
retryer.call(() -> {
doSomething();
return null;
});
} catch (ExecutionException | RetryException e) {
e.printStackTrace();
}
}
复制代码