The guava-retrying module provides a general purpose method for retrying arbitrary Java code with specific stop, retry, and exception handling capabilities that are enhanced by Guava's predicate matching.
com.github.rholder
guava-retrying
2.0.0
简单的demo
Callable callable = new Callable() {
public Boolean call() throws Exception {
return true; // do something useful here
}
};
Retryer retryer = RetryerBuilder.newBuilder()
.retryIfResult(Predicates.isNull())
.retryIfExceptionOfType(IOException.class)
.retryIfRuntimeException()
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
try {
retryer.call(callable);
} catch (RetryException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
首先重试的内容主体必须是一个Callable
的实现,重试是根据其call
方法的执行状态(异常、返回值)定制的重试策略来进行重试的。
比如上面的列子。重试者retryer
由建造者RetryerBuilder
创建的,RetryerBuilder提供了各种方法来定制重试策略。
根据返回是否是null来决定是否重试
.retryIfResult(Predicates.isNull())
根据异常类型是否是IOException来决定是否重试
.retryIfExceptionOfType(IOException.class)
多个类型的时候还可以使用Predicate
Retryer retryer1 = RetryerBuilder.newBuilder()
.retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class),
Predicates.instanceOf(IllegalStateException.class)))
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
根据异常是否是RuntimeException来决定是否重试
.retryIfRuntimeException()
设置重试的终止策略,尝试3次后终止
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
除了上述重试判断外判断重试的方式与策略还有很多,比如
返回值匹配
.retryIfResult(Predicates.containsPattern("_error$"))
.retryIfResult(Predicates.equalTo(2))
常用的终止策略在com.github.rholder.retry.StopStrategies
中。
尝试3次后终止
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
30s后终止
.withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS))
不终止
.withStopStrategy(StopStrategies.neverStop())
很多场景下并不是立即重试(环境影响,立即重试的失败率依旧很高),一般会等待一段时间后继续重试。
提供了等待策略,常用策略在com.github.rholder.retry.WaitStrategies
中。
比如
等待固定时间
.withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS))
每次等待时间递增
.withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS,1,TimeUnit.SECONDS))
斐波那契式等待
.withWaitStrategy(WaitStrategies.fibonacciWait())
guava-retrying
提供每次重试的监听机制,每次重试后会回调注册的监听者,按顺序依次执行。
监听者必须实现RetryListener.onRetry
的方法。参数attempt
是每次尝试的记录。
public class MyRetryListener implements RetryListener {
@Override
public void onRetry(Attempt attempt) {
// 第几次重试,(注意:第一次重试其实是第一次调用)
System.out.print("[retry]time=" + attempt.getAttemptNumber());
// 距离第一次重试的延迟
System.out.print(",delay=" + attempt.getDelaySinceFirstAttempt());
// 重试结果: 是异常终止, 还是正常返回
System.out.print(",hasException=" + attempt.hasException());
System.out.print(",hasResult=" + attempt.hasResult());
// 是什么原因导致异常
if (attempt.hasException()) {
System.out.print(",causeBy=" + attempt.getExceptionCause().toString());
} else {
// 正常返回时的结果
System.out.print(",result=" + attempt.getResult());
}
// bad practice: 增加了额外的异常处理代码
try {
Boolean result = attempt.get();
System.out.print(",rude get=" + result);
} catch (ExecutionException e) {
System.err.println("this attempt produce exception." + e.getCause().toString());
}
System.out.println();
}
}
增加监听者(可以增加多个,顺序执行)
.withRetryListener(new MyRetryListener<>())
.withRetryListener(new RetryListener() {
@Override
public void onRetry(Attempt attempt) {
if (attempt.hasException()){
attempt.getExceptionCause().printStackTrace();
}
}
})
long startTime = System.nanoTime();
for (int attemptNumber = 1; ; attemptNumber++) {
Attempt attempt;
try {
// 执行成功
V result = attemptTimeLimiter.call(callable);
attempt = new ResultAttempt(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
} catch (Throwable t) {
// 执行失败
attempt = new ExceptionAttempt(t, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
}
// 监听器处理
for (RetryListener listener : listeners) {
listener.onRetry(attempt);
}
// 是否符合终止策略
if (!rejectionPredicate.apply(attempt)) {
return attempt.get();
}
// 是否符合停止策略
if (stopStrategy.shouldStop(attempt)) {
throw new RetryException(attemptNumber, attempt);
} else {
// 计算下次重试间隔时间
long sleepTime = waitStrategy.computeSleepTime(attempt);
try {
blockStrategy.block(sleepTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RetryException(attemptNumber, attempt);
}
}
}
一次执行任务
单次任务执行时间限制(如果单次任务执行超时,则终止执行当前任务)
任务阻塞策略(通俗的讲就是当前任务执行完,下次任务还没开始这段时间做什么)
默认策略为:BlockStrategies.THREAD_SLEEP_STRATEGY 也就是调用 Thread.sleep(sleepTime);
重试异常
自定义重试监听器,可以用于异步记录错误日志;
停止重试策略,提供三种
等待时长策略(控制时间间隔),返回结果为下次执行时长