背景
之前很少接触需要重试的东西,失败后直接抛异常。但是接触过分布式锁以后,未获取锁的异常属于正常现象,需要我们捕获后重试,经过设定的重试次数后如果再抛异常,就直接抛给上层。
实现1
第一种方法是直接在代码使用循环来实现重试。
public void test(RegisterParam registerParam){
int times = 3;
for (int i=0;i<times;i++) {
try {
this.registerWithRetry(registerParam);
break;
} catch (InterruptedException e) {
// 重试
} catch (ServiceException e) {
// 重试
}
}
}
正常逻辑和重试逻辑强耦合,重试逻辑非常依赖正常逻辑的执行结果,对正常逻辑预期结果被动重试触发,对于重试根源往往由于逻辑复杂被淹没,可能导致后续运维对于重试逻辑要解决什么问题产生不一致理解。重试正确性难保证而且不利于运维,原因是重试设计依赖正常逻辑异常或重试根源的臆测。
设计模式实现重试
上面的一个问题就是正常的逻辑和重试代码强耦合在一起,可以通过命令模式来解耦。将要执行的业务抽象成一个命令,将命令传到Retry里,不需要业务代码关心重试,可以实现解耦。
Guava-Retry
guava-retry是guava提供的重试机制,使用起来很简单
引入pom
<guava-retry.version>2.0.0</guava-retry.version>
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>${guava-retry.version}</version>
</dependency>
构造重试对象
Retryer<Boolean> retryer = RetryerBuilder
.<Boolean>newBuilder()
// 如果结果是false,重试
.retryIfResult(Predicates.equalTo(false))
// 本次尝试失败后,过3秒再次尝试
.withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))
// 总共尝试3次
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
// 遇到运行时异常才重试
.retryIfRuntimeException()
.build();
使用
retryer.call(() -> this.registerWithRetry(registerParam));
网友评论