Guava Retrying解析

守給你的承諾、 提交于 2019-12-22 17:38:37

【推荐】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

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!