In normal development, we often encounter the need to call the interface and external services, but some interface services cannot immediately return data, but need to process a period of time to return the real business data, if not finished processing will directly return an intermediate state of the result.

Business scenarios:

  1. There are dependency unstable scenarios in the code that require retries to obtain the expected results or try to re-execute the logic without ending immediately, such as remote interface access, data load access, data upload verification, etc

  2. In cases where exceptions require retries, you also want to decouple the normal logic from the retry logic

  3. The peer interface does not support asynchronous callback

The interface returns a result similar to {“errorCode”:”-1″, “errorMsg”:” processing “, “result”:””}, and the caller needs to call it again after several seconds to obtain the real result.

The traditional way to write this is as follows (pseudocode) :

int time = 0; do { time++; result = testRedo(); } while (null == result && time < 5);Copy the code

This scenario is more or less common, but there are several obvious drawbacks to the above:

  1. The caller needs to consider not only the number of calls, but also the interval time of each call, so as to obtain the final result with the least call as far as possible (one more request means one more network overhead, which is not convenient for real-time adjustment).
  2. Occasionally, an exception occurs in one invocation (interface error or network exception). If there is no exception handling, the remaining invocation times will be affected and high availability cannot be guaranteed
  3. The above code has concurrency problems in multithreaded cases, because the first call may not be the first to return, but it may be later calls that return first, resulting in unexpected results
  4. Performance issues, if you use multithreading to consider thread creation, destruction and switching issues

Of course these problems can be solved by implementing them themselves, but there are already wheels that we can use directly

Solution:

In these scenarios, You can use Google’s Guava-Retry tool. The features of Guava-Retryer are as follows:

  1. Supports retry times and retry interval, retry policies, and delay policies in complex scenarios
  2. Multiple exception or custom entity object retry sources are supported, giving the retry function more flexibility
  3. Thread-safe, thread-pool-based isolation, we just need to focus on our business logic implementation
  4. Threads are managed internally using thread pools
  5. Using chain calls based on command mode, easy to use POM dependencies:
< the dependency > < groupId > com. Making. Rholder < / groupId > < artifactId > guava - retrying < / artifactId > < version > 2.0.0 < / version > </dependency>Copy the code

Git address:Github.com/rholder/gua…

The following code simulates the interface call and sets the number of retries to 5, with each call interval of 2 seconds, and if an exception occurs during the call or the result meets the retry condition, the call will be called again until the maximum number of times (the exception is thrown) :

// Predicate<String> condition = Response -> objects.nonnull (response) && "processing ".equals(response.getreturnCode ()); Optional<String> Response = retryutil. retry(condition,) -> invoke(request), // try again at 500ms to make configuration 3); Return response.orelse (null); return response.orelse (null);Copy the code

RetryUtilIs I amguava-retryingEncapsulation implementation, the following code we can directly take to use, just need to change according to the business retry conditions and retry task and retry interval and times:

/** * Perform the task repeatedly based on the input condition, and return null if the input condition exceeds retryTimes. The retry times, retry time, and retry exception are recorded in log * * @param condition. For example, the interface returns an errorCode that is in process or not required @param retryTimes Number of retries * @return targetBean */ public static <V> Optional<V> retry(Predicate<V> condition, Callable<V> task, int sleepTime, int retryTimes) { Optional<V> result = Optional.empty(); StopWatch stopWatch = new StopWatch(); try { stopWatch.start(); Retryer<V> Retry = RetryerBuilder.<V>newBuilder() // Default automatic retry when an exception occurs during task execution. RetryIfException () // Retry conditions (based on service scenarios) .retryifResult (condition) withWaitStrategy(WaitStrategies. FixedWait (sleepTime, TimeUnit. MILLISECONDS)) / / retry strategy. WithStopStrategy (StopStrategies. StopAfterAttempt (retryTimes)) / / retry listeners WithRetryListener (new RetryListener() {@override public <V> void onRetry(Attempt<V> Attempt) {// Records the retry times and exception information log.info(MessageFormat.format("{0}th retry", attempt.getAttemptNumber())); if (attempt.hasException()) { log.error(MessageFormat.format("retry exception:{0}", attempt.getExceptionCause())); } } }).build(); Result = option.ofNullable (retry.call(task)); } catch (Exception e) { log.error("retry fail:", e.getMessage()); } finally { stopWatch.stop(); log.info("retry execute time", stopWatch.getTime()); } return result; }Copy the code

The retry interval and retry times can be configured to facilitate subsequent adjustment based on log observations

Retry policies and apis:

  • AttemptTimeLimiter: Time limit for executing a single task (If the execution of a single task times out, the current task is terminated)
  • BlockStrategies: task blocking strategy. The default strategy is blockstrategies.thread_sleep_strategy, which calls thread.sleep ().
  • StopStrategy: Stop retry policy, which provides three types:
  • StopAfterDelayStrategyFor example, the maximum execution time is set to 10 seconds. If the retry time exceeds the maximum, the task is terminated and RetryException is returned
  • NeverStopStrategy: do not stop, used in cases where polling is required until the desired result is returned
  • StopAfterAttemptStrategy: Sets the maximum number of retries. If the number exceeds the maximum number of retries, the system stops retries and returns a retry exception
  • WaitStrategy: Wait time policy (control interval), and the result is the next execution time:
  • FixedWaitStrategy: fixed waiting duration policy
  • RandomWaitStrategy: Random waiting duration policy
  • IncrementingWaitStrategy: incremental waiting duration policy
  • ExponentialWaitStrategy: indicates the wait time policy
  • FibonacciWaitStrategy: Fibonacci wait time strategy
  • ExceptionWaitStrategy: Indicates the waiting policy for abnormal duration
  • CompositeWaitStrategyIn addition to Google’s Retry, the Spring framework provides a retry tool:spring-retry, which customizes the retry operation template. There is a Retry API in RxJava for similar use.

Source: javakk.com/141.html