Article Directory
-
- Guava-retrying
-
- 1. Main related classes
- 2. WaitStrategies retry waiting strategy
-
- 2.1 ExponentialWaitStrategy Exponential waiting strategy (WaitStrategies.exponentialWait)
- 2.2 FibonacciWaitStrategy Fibonacci waiting strategy (WaitStrategies.fibonacciWait)
- 2.3 FixedWaitStrategy Fixed duration waiting strategy (WaitStrategies.fixedWait)
- 2.4 RandomWaitStrategy Random waiting strategy (WaitStrategies.randomWait)
- 2.5 IncrementingWaitStrategy Incremental waiting strategy (WaitStrategies.incrementingWait)
- 2.6 ExceptionWaitStrategy exception waiting strategy (WaitStrategies.exceptionWait)
- 2.7 CompositeWaitStrategy composite waiting strategy 【***】(WaitStrategies.join)
- 3. StopStrategies retry stop strategy
- 4 AttemptTimeLimiters task execution time limit (withAttemptTimeLimiter)
- 5. BlockStrategies blocking strategy
- 6. Package use
Guava-retrying
When calling external services or related interfaces, external services are generally unreliable for calling, especially in the current microservices. In the case of a poor network environment, network jitter is very easy to cause abnormal conditions such as request timeouts . In this case, a failure retry is required to re-call the API for fault tolerance. The retry strategy is widely used in service governance, and it is generally checked whether the service is alive through regular detection.
Guava Retrying
It is an extension package of the Google Guava library. It can create a configurable retry machine for any function call. It is a flexible and convenient retry component that includes a variety of retry strategies . The most important thing is that it is very easy to expand . Define a retry mechanism to resolve various unstable factors in the system while monitoring the results and behavior of each retryGuava-retrying
gitHub address: https://github.com/rholder/guava-retrying
1. Main related classes
1.1 Attemp class
Attemp
** It is a task retry (call) and the result of a request**, which mainly saves the number of retries of the current request , whether it contains exceptions , and the request return value .Generally used with a listener to handle the details of the retry process
Attemp method | describe |
---|---|
long getAttemptNumber() |
The current number of retries (the number of retries) starts from 1 |
long getDelaySinceFirstAttempt() |
Delay time from the first retry, that is, the time difference from the first retry, in milliseconds |
boolean hasException() |
Judging whether there is an exception (you can retry according to the exception/special result value) |
boolean hasResult() |
Determine whether to return the data result (retry to meet the special result value) |
Throwable getExceptionCause() throws IllegalStateException |
Abnormal retry data, get exception information |
V getResult() throws IllegalStateException |
Get retry result information |
V get() throws ExecutionException |
Similar to getResult() returning retry results, but handles exceptions differently |
1.2 Retryer class
Retryer
Is the core class , used to implement the retry strategy , a bunch of classes areRetryerBuilder
constructed (Factory creator), andRetryerBuilder
is responsible for adding the set retry strategy toRetryer
, and finally implements the retry strategy by executingRetryer
the core methodcall
The approximate process is as follows:
- Determine whether the task duration limit is exceeded
- Execute retry listener
- Determine whether the retry condition is met
- retry stop policy
- retry wait policy
- blocking strategy
call
The method source code is as follows: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); } } } }
RetryerBuilder
It is a Factory creator that can customize the retry source and support multiple retry sources. It can setException
exception objects and custom assertion objects throughretryIfException
andretryIfResult
, and supports multiple and compatible.
RetryerBuilder Properties | describe |
---|---|
retryIfExceptionOfType(Class<? extends Throwable> exceptionClass) |
Only retry when certain exceptions occur, such as NullPointerException :retryIfExceptionOfType(Exception.class); |
Predicate achieved byretryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class) |
Predicates.instanceOf(NullPointerException.class))) |
retryIfException |
Throwing runtime exception, checked exception will be retried, but throwing error will not retry |
retryIfRuntimeException |
runtime Retry when an exception is thrown , neither checked exception error nor retry |
retryIfExceptionOfType(Error.class) |
Only throw an error to retry |
retryIfResult You can specify Callable the method to retry when it returns a value |
|
retryIfResult(Predicates.equalTo(false)) |
go back false try again |
retryIfResult(Predicates.containsPattern("_customInfo$")) |
_customInfo Retry only after ending with |
1.3 RetryListener
When the listener executes
call
the method, it will call the method in the listener RetryListeneronRetry
, and implement the custom retry logic mechanism by implementing and rewriting this class.
@Beta
public interface RetryListener {
// 监听方法
<V> void onRetry(Attempt<V> var1);
}
2. WaitStrategies retry waiting strategy
When the execution fails, use to
WaitStrategies
specify different waiting strategies to retry the nth time
withWaitStrategy
Different retry waiting strategies can be set through the method, and the common strategies are as follows:
2.1 ExponentialWaitStrategy Exponential waiting strategy (WaitStrategies.exponentialWait)
Exponential compensation algorithm implementation ( wikipedia Exponential Backoff ), calculate the waiting time according to the number of retries, the source code is as follows:
@Override public long computeSleepTime(Attempt failedAttempt) { double exp = Math.pow(2, failedAttempt.getAttemptNumber()); long result = Math.round(multiplier * exp); if (result > maximumWait) { result = maximumWait; } return result >= 0L ? result : 0L; }
// 默认倍数(乘) multiplier = 1,最大值是 Long.MAX_VALUE RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.exponentialWait()); // 指定乘数multiplier 和 最大值,第一次失败后,依次等待时长:2^1 * 100、2^2 * 100、2^3 * 100...直到最多5分钟。 // 5分钟后,每隔5分钟重试一次 // 3个参数, multiplier: 乘数, maximumTime: 最大等待时长, maximumTimeUnit: 最大等待时长单位 RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES));
2.2 FibonacciWaitStrategy Fibonacci waiting strategy (WaitStrategies.fibonacciWait)
After failure, wait according to the Fibonacci sequence
// 默认乘数multiplier是1,最大值是Long.MAX_VALUE RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.fibonacciWait()); // 指定乘数multiplier 和 最大值,第一次失败后,依次等待时长,1*100、1*100、2*100、3*100、5*100...直到最多5分钟,5分钟后每隔5分钟重试一次 // 3个参数, multiplier: 乘数, maximumTime: 最大等待时长, maximumTimeUnit: 最大等待时长单位 RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.fibonacciWait(100, 5, TimeUnit.MINUTES));
2.3 FixedWaitStrategy Fixed duration waiting strategy (WaitStrategies.fixedWait)
After a failure, it will wait for a fixed amount of time to retry
// 每 100 ms 重试一次 RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.fixedWait(100, TimeUnit.MILLISECONDS));
2.4 RandomWaitStrategy Random waiting strategy (WaitStrategies.randomWait)
By setting the time interval of random waiting, or the maximum length of random waiting, take a random number from it, and retry at a random time
// 最大随机时长10s RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.randomWait(10, TimeUnit.SECONDS)); // 随机区间配置,[2, 10] 2-10s随机等待,四个参数分别为: // minimumTime: 最小值,minimumTimeUnit: 最小值单位; maximumTime: 最大值, maximumTimeUnit: 最大值单位 RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.randomWait(2, TimeUnit.SECONDS, 10, TimeUnit.SECONDS));
2.5 IncrementingWaitStrategy Incremental waiting strategy (WaitStrategies.incrementingWait)
According to the initial value and incremental value, the waiting time increases sequentially
// 递增配置,初始2s,后面每次在前面的基础上加3s,等待时长: 2、5、8、11、14、17、20 // 四个参数 >>> initialSleepTime: 初始等待时长,initialSleepTimeUnit: 初始等待时长单位, increment: 递增时长值, incrementTimeUnit: 递增时长单位 RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.incrementingWait(2, TimeUnit.SECONDS, 3, TimeUnit.SECONDS))
2.6 ExceptionWaitStrategy exception waiting strategy (WaitStrategies.exceptionWait)
Specify the waiting time for retrying according to the configuration exception information. If the exception does not match, the waiting time is 0
// 当出现空指针异常时,等待1s,出现数组越界异常时等待2s (可以配置多个) // 参数: exceptionClass: 异常类,Function<T, Long> function: 处理函数,出现对应异常,返回等待时长 RetryerBuilder<Object> builder = RetryerBuilder.newBuilder(); builder.withWaitStrategy(WaitStrategies.exceptionWait(NullPointerException.class, e -> 1000L)); builder.withWaitStrategy(WaitStrategies.exceptionWait(ArrayIndexOutOfBoundsException.class, e -> 2000L));
2.7 CompositeWaitStrategy composite waiting strategy 【***】(WaitStrategies.join)
When one or more waiting policies are satisfied at the same time, the waiting time is the sum of all waiting policies. For example:
// 固定时长策略 + 异常等待策略,对于空指针异常,等待3s,其它情况等待2s // join 组合多个策略 RetryerBuilder.newBuilder().withWaitStrategy( WaitStrategies.join(WaitStrategies.exceptionWait(NullPointerException.class, e -> 1000L), WaitStrategies.fixedWait(2, TimeUnit.SECONDS)));
3. StopStrategies retry stop strategy
Stop retrying after specifying the number of retries . It is best to configure them all, otherwise there may be unlimited retries .
withStopStrategy
You can set the retry stop strategy by the method
3.1 NeverStopStrategy unlimited retry (StopStrategies.neverStop)
// 无限次数重试,谨慎使用 RetryerBuilder.newBuilder().withStopStrategy(StopStrategies.neverStop());
3.2 StopAfterAttemptStrategy Retry the specified number of times to stop (StopStrategies.stopAfterAttempt)
// 重试五次结束 RetryerBuilder.newBuilder().withStopStrategy(StopStrategies.stopAfterAttempt(5));
3.3 StopAfterDelayStrategy ends after retrying for a specified period of time (StopStrategies.stopAfterDelay)
// 10s重试,超过10s结束 RetryerBuilder.newBuilder().withStopStrategy(StopStrategies.stopAfterDelay(10, TimeUnit.SECONDS));
4 AttemptTimeLimiters task execution time limit (withAttemptTimeLimiter)
Indicates the execution time limit of a single task [if a single task execution timeout occurs, the execution of the current retry task will be terminated], and the
withAttemptTimeLimiter
execution time limit of the task is set by the method
4.1 FixedAttemptTimeLimit specifies the execution time limit (AttemptTimeLimiters.fixedTimeLimit)
Specify the execution time limit of the task. In order to control thread management, it is best to specify the corresponding thread pool
// 重试方法超过2s中断 RetryerBuilder.newBuilder().withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS)); RetryerBuilder.newBuilder().withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS), Executors.newCachedThreadPool());
4.2 NoAttemptTimeLimit No time limit (AttemptTimeLimiters.noTimeLimit)
RetryerBuilder.newBuilder().withAttemptTimeLimiter(AttemptTimeLimiters.noTimeLimit())
5. BlockStrategies blocking strategy
In the process of retrying and waiting, the execution is blocked according to the time calculated by the waiting strategy. By
default, only one blocking strategy is provided:ThreadSleepStrategy
, and the implementation method is realized throughThread.sleep()
the sleep method [Advantages of the sleep method: can respond to external interrupt requests]The default blocking strategy is thread sleep, and you can customize the blocking strategy. Here, a spin lock is used to implement it without blocking the thread.
public class GuavaBlockStrategy implements BlockStrategy { @Override public void block(long sleepTime) throws InterruptedException { long start = System.currentTimeMillis(); long end = start; while (end - start <= sleepTime) { end = System.currentTimeMillis(); } LogUtil.info("block end", start, end, sleepTime); } }
use:
// 自定义阻塞策略:自旋锁实现 RetryerBuilder.newBuilder().withBlockStrategy(new SpinBlockStrategy());
6. Package use
Actual project use reference: Guava retry package use