Guava-retrying retry mechanism

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 RetryingIt 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 retry
Guava-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 are RetryerBuilder constructed (Factory creator), and RetryerBuilder is responsible for adding the set retry strategy to Retryer, and finally implements the retry strategy by executing Retryerthe core methodcall


The approximate process is as follows:

  1. Determine whether the task duration limit is exceeded
  2. Execute retry listener
  3. Determine whether the retry condition is met
  4. retry stop policy
  5. retry wait policy
  6. blocking strategy

callThe 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);
                }
            }
        }
    }

RetryerBuilderIt is a Factory creator that can customize the retry source and support multiple retry sources. It can set Exception exception objects and custom assertion objects through retryIfExceptionand retryIfResult , 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);
Predicateachieved by
retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class)
Predicates.instanceOf(NullPointerException.class)))
retryIfException Throwing runtimeexception, checkedexception will be retried, but throwing errorwill not retry
retryIfRuntimeException runtimeRetry when an exception is thrown , neither checkedexception errornor retry
retryIfExceptionOfType(Error.class) Only throw an error to retry
retryIfResultYou can specify Callablethe method to retry when it returns a value
retryIfResult(Predicates.equalTo(false)) go back falsetry again
retryIfResult(Predicates.containsPattern("_customInfo$")) _customInfoRetry only after ending with

1.3 RetryListener

When the listener executes callthe method, it will call the method in the listener RetryListener onRetry, 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

withWaitStrategyDifferent 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 . withStopStrategyYou 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 withAttemptTimeLimiterexecution 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 through Thread.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

おすすめ

転載: blog.csdn.net/qq_40542534/article/details/128212025