【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
一、重试场景
远程调用遇到限流或者超时,但是在并发的情况下需要考虑请求的幂等,不是这里要讨论的问题
1. 什么情况下重试 -- 有异常或者返回Success是false时
重试策略:异常校验、结果校验
2. 什么情况下结束 -- 重试3次
终止策略
3. 等待多久重试 -- 200ms
等待策略
4. 监听重试过程
二、 基本用法
Retryer<Boolean> retryer = RetryerBuilder
.<Boolean>newBuilder()
// 抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。
.retryIfException()
// 自定义 指定返回值 也需要重试:返回false也需要重试
.retryIfResult(Predicates.equalTo(false))
// 重试时间间隔
.withWaitStrategy(WaitStrategies.fixedWait(200, TimeUnit.MILLISECONDS))
// 尝试次数
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.build();
retryer.call(() -> {});
1. 重试者retryer由建造者RetryerBuilder创建的,RetryerBuilder提供了各种方法来定制重试策略。
2. 重试的内容主体必须是一个Callable的实现,重试是根据其call方法的执行状态(异常、返回值)定制的重试策略来进行重试的。
.retryIfResult(Predicates.containsPattern("_error$"))
.retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class),
Predicates.instanceOf(IllegalStateException.class)))
3. 终止策略
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS))
.withStopStrategy(StopStrategies.neverStop())
4. 等待策略
.withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS))
.withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS,1,TimeUnit.SECONDS))
.withWaitStrategy(WaitStrategies.fibonacciWait())
5. 重试监听
每次重试后会回调注册的监听者,按顺序依次执行。监听者必须实现RetryListener.onRetry的方法
6. 线程池和时间限制
.withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS))
7. 执行异常
RetryException:执行终止,或者线程被interrupt
ExecutionException:Callable执行异常没有被重试
三、代码解析
1. RetryerBuilder
private AttemptTimeLimiter<V> attemptTimeLimiter;
private StopStrategy stopStrategy;
private WaitStrategy waitStrategy;
private BlockStrategy blockStrategy;
private Predicate<Attempt<V>> rejectionPredicate = Predicates.alwaysFalse();
private List<RetryListener> listeners = new ArrayList<RetryListener>();
/**
* Builds the retryer.
*
* @return the built retryer.
*/
public Retryer<V> build() {
AttemptTimeLimiter<V> theAttemptTimeLimiter = attemptTimeLimiter == null ? AttemptTimeLimiters.<V>noTimeLimit() : attemptTimeLimiter;
StopStrategy theStopStrategy = stopStrategy == null ? StopStrategies.neverStop() : stopStrategy;
WaitStrategy theWaitStrategy = waitStrategy == null ? WaitStrategies.noWait() : waitStrategy;
BlockStrategy theBlockStrategy = blockStrategy == null ? BlockStrategies.threadSleepStrategy() : blockStrategy;
return new Retryer<V>(theAttemptTimeLimiter, theStopStrategy, theWaitStrategy, theBlockStrategy, rejectionPredicate, listeners);
}
2. AttemptTimeLimiter
直接执行
private static final class NoAttemptTimeLimit<V> implements AttemptTimeLimiter<V> {
@Override
public V call(Callable<V> callable) throws Exception {
return callable.call();
}
}
线程池执行,执行时间限制
private static final class FixedAttemptTimeLimit<V> implements AttemptTimeLimiter<V> {
private final TimeLimiter timeLimiter;
private final long duration;
private final TimeUnit timeUnit;
@Override
public V call(Callable<V> callable) throws Exception {
return timeLimiter.callWithTimeout(callable, duration, timeUnit, true);
}
}
SimpleTimeLimiter :ExecutorService
@Override
public <T> T callWithTimeout(Callable<T> callable, long timeoutDuration,
TimeUnit timeoutUnit, boolean amInterruptible) throws Exception {
checkNotNull(callable);
checkNotNull(timeoutUnit);
checkArgument(timeoutDuration > 0, "timeout must be positive: %s",
timeoutDuration);
Future<T> future = executor.submit(callable);
try {
if (amInterruptible) {
try {
return future.get(timeoutDuration, timeoutUnit);
} catch (InterruptedException e) {
future.cancel(true);
throw e;
}
} else {
return Uninterruptibles.getUninterruptibly(future,
timeoutDuration, timeoutUnit);
}
} catch (ExecutionException e) {
throw throwCause(e, true);
} catch (TimeoutException e) {
future.cancel(true);
throw new UncheckedTimeoutException(e);
}
}
3. Retryer
/**
* Executes the given callable. If the rejection predicate
* accepts the attempt, the stop strategy is used to decide if a new attempt
* must be made. Then the wait strategy is used to decide how much time to sleep
* and a new attempt is made.
*
* @param callable the callable task to be executed
* @return the computed result of the given callable
* @throws ExecutionException if the given callable throws an exception, and the
* rejection predicate considers the attempt as successful. The original exception
* is wrapped into an ExecutionException.
* @throws RetryException if all the attempts failed before the stop strategy decided
* to abort, or the thread was interrupted. Note that if the thread is interrupted,
* this exception is thrown and the thread's interrupt status is set.
*/
public V call(Callable<V> callable) throws ExecutionException, RetryException {
long startTime = System.nanoTime();
for (int attemptNumber = 1; ; attemptNumber++) {
Attempt<V> attempt;
try {
V result = attemptTimeLimiter.call(callable);
attempt = new ResultAttempt<V>(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
} catch (Throwable t) {
attempt = new ExceptionAttempt<V>(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);
}
}
}
}
Attempt 一次执行任务
ResultAttempt Result执行结果, 当前执行次数,当前执行时长
ExceptionAttempt Throwable执行异常, 当前执行次数,当前执行时长
Predicate 判断条件
AndPredicate OrPredicate
来源:oschina
链接:https://my.oschina.net/u/4208224/blog/3145926