1, Retry scenario
The remote call encounters current restriction or timeout, but the idempotent of the request needs to be considered in the case of concurrency, which is not the problem to be discussed here
1. When to retry -- when there is an exception or Success is false
Retry policy: exception verification and result verification
2. When does it end? 3 retries
Termination policy
3. How long to wait? 200ms
Wait policy
4. Monitor retry process
2, Basic usage
Retryer<Boolean> retryer = RetryerBuilder .<Boolean>newBuilder() // If a runtime exception or checked exception is thrown, it will be retried, but if an error is thrown, it will not be retried. .retryIfException() // Custom specified return value also needs to be retried: return false also needs to be retried .retryIfResult(Predicates.equalTo(false)) // Retry interval .withWaitStrategy(WaitStrategies.fixedWait(200, TimeUnit.MILLISECONDS)) // Number of attempts .withStopStrategy(StopStrategies.stopAfterAttempt(3)) .build(); retryer.call(() -> {});
1. Retryer is created by retryer builder. Retryer builder provides various methods to customize retrying strategy.
2. The content body of the retry must be a Callable implementation. The retry is based on the retry policy customized by the execution status (exception, return value) of the call method.
.retryIfResult(Predicates.containsPattern("_error$"))
.retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class), Predicates.instanceOf(IllegalStateException.class)))
3. Termination strategy
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS))
.withStopStrategy(StopStrategies.neverStop())
4. Waiting strategy
.withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS))
.withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS,1,TimeUnit.SECONDS))
.withWaitStrategy(WaitStrategies.fibonacciWait())
5. Retry listening
After each retry, the registered listener will be called back and executed in sequence. Listener must implement method of RetryListener.onRetry
6. Thread pool and time limit
.withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS))
7. Execution exception
RetryException: execution terminated or thread interrupt ed
ExecutionException: Callable execution exception was not retried
3, Code parsing
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
Direct execution
private static final class NoAttemptTimeLimit<V> implements AttemptTimeLimiter<V> { @Override public V call(Callable<V> callable) throws Exception { return callable.call(); } }
Thread pool execution, execution time limit
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 to perform a task at a time
ResultAttempt Result execution result, current execution times, current execution time
ExceptionAttempt Throwable execution exception, current execution times, current execution time
Predict judgment conditions
AndPredicate OrPredicate