Detailed Explanation of Spring Retry

  • 2021-12-11 18:01:01
  • OfStack

Directory 1 Introduction to Spring-Retry Application of 2 Spring 1 Import maven coordinates
2 Add the called class
3 Adding Test Classes
Application of 3 SpringBoot 1 Import maven coordinates
2 Add 1 management class
3 Add annotation on startup class @ EnableRetry
4 Adding Test Classes

1 Introduction of Spring-Retry

In a few daily scenarios, many operations need to be retried. spring-retry is a retry framework based on spring provided by spring, which is very simple and easy to use.

Application of 2 Spring

1 Import maven coordinates


 <dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.2.RELEASE</version>
 </dependency>

2 Add the called class


@Slf4j
public class RetryDemo {

    public static boolean retryMethod(Integer param) {
        int i = new Random().nextInt(param);
        log.info(" Randomly generated number :{}", i);

        if (1 == i) {
            log.info(" For 1, Return true.");
            return true;
        } else if (i < 1) {
            log.info(" Less than 1, Throw parameter exception .");
            throw new IllegalArgumentException(" Parameter exception ");
        } else if (i > 1 && i < 10) {
            log.info(" Greater than 1, Less than 10, Throw parameter exception .");
            return false;
        } else {
            // For other 
            log.info(" Greater than 10, Throw a custom exception .");
            throw new RemoteAccessException(" Greater than 10, Throw a custom exception ");
        }
    }
}

3 Adding Test Classes


@Slf4j
public class SpringRetryTest {

    /**
     *  Retry interval ms, Default 1000ms
     */
    private long fixedPeriodTime = 1000L;
    /**
     *  Maximum number of retries , Default to 3
     */
    private int maxRetryTimes = 3;
    /**
     *  Indicates which exceptions need to be retried 
     * key1 Must be Throwable Subclass of exception     Class<? extends Throwable>
     * value For true Indicates that a retry is required 
     */
    private Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();


    @Test
    public void test() {

        // 1  Add the processing result of exception  true Retry is required  false No retry is required 
        exceptionMap.put(RemoteAccessException.class, true);

        // 2  Build an instance of the retry template 
        RetryTemplate retryTemplate = new RetryTemplate();

        // 3  Set the retry fallback operation policy    Set the retry interval 
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(fixedPeriodTime);

        // 4  Setting Retry Policy    Set the number of retries   Set exception handling results 
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap);

        //5  Retry template adds retry policy   Add a fallback action policy 
        retryTemplate.setRetryPolicy(retryPolicy);
        retryTemplate.setBackOffPolicy(backOffPolicy);
    
        // 6  Invoke method 
        Boolean resp = retryTemplate.execute(
                // RetryCallback  Retry callback method 
                retryContext -> {
                    boolean result = RetryDemo.retryMethod(110);
                    log.info(" Method returns the result = {}", result);
                    return result;
                },
                // RecoveryCallback  Exception callback method 
                retryContext -> {
                    //
                    log.info(" Maximum number of retries exceeded or an undefined exception was thrown !!!");
                    return false;
                }
        );

        log.info(" Interface returns results  = {}",resp);

    }

}
/*
 //  View the results 
 [main] INFO com.cf.demo.SpringRetry.SpringRetryTest -  Maximum number of retries exceeded or an undefined exception was thrown !!!
 [main] INFO com.cf.demo.SpringRetry.SpringRetryTest -  Interface returns results  = false
*/

As you can see from the written annotations of the code, The RetryTemplate object is the retry executor of the Spring-Retry framework, Which adds a retry policy, Fallback operation strategy, etc. (Note Step 5). RetryTemplate retry method (Note Step 6). Through execute method, the incoming parameters are retry callback logical object RetryCallback and recovery object RecoveryCallback at the end of execution operation. And you can switch the added exception types, and know that only the corresponding exception has been added, will trigger retry operation, otherwise directly call RecoveryCallback object method.

RetryTemplate part of the source code:


 /**
  * Keep executing the callback until it either succeeds or the policy dictates that we
  * stop, in which case the recovery callback will be executed.
  *
  * @see RetryOperations#execute(RetryCallback, RecoveryCallback)
  * @param retryCallback the {@link RetryCallback}
  * @param recoveryCallback the {@link RecoveryCallback}
  * @throws TerminatedRetryException if the retry has been manually terminated by a
  * listener.
  */
 @Override
 public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback,
   RecoveryCallback<T> recoveryCallback) throws E {
  return doExecute(retryCallback, recoveryCallback, null);
 }

RetryTemplate Add Retry Policy Source:


 /**
  * Setter for {@link RetryPolicy}.
  *
  * @param retryPolicy the {@link RetryPolicy}
  */
 public void setRetryPolicy(RetryPolicy retryPolicy) {
  this.retryPolicy = retryPolicy;
 }

Implementation classes for the RetryPolicy interface:

AlwaysRetryPolicy: Allows unlimited retries until success, possibly resulting in an infinite loop

CircuitBreakerRetryPolicy: Retry strategy with fuse function, which needs to set three parameters openTimeout, resetTimeout and delegate

CompositeRetryPolicy: There are two combinations of combined retry strategies. Optimistic combined retry strategy means that you can retry as long as you have one strategy to allow it.
Pessimistic combinatorial retry strategy means that if one strategy is not allowed, it can be retried, but no matter which combination mode, every one strategy in the combination will be executed

ExceptionClassifierRetryPolicy: Set the retry strategy for different exceptions, similar to the combined retry strategy. The difference is that only the retries for different exceptions are distinguished here

NeverRetryPolicy: Only calls to RetryCallback1 are allowed, no retries are allowed

SimpleRetryPolicy: Fixed number of retry policy, default maximum number of retries is 3, RetryTemplate default policy

TimeoutRetryPolicy: A timeout retry policy, with a default timeout of 1 second, allowing retries within the specified timeout

RetryTemplate Add Rollback Policy Source:


 /**
  * Setter for {@link BackOffPolicy}.
  *
  * @param backOffPolicy the {@link BackOffPolicy}
  */
 public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
  this.backOffPolicy = backOffPolicy;
 }

Implementation classes for BackOffPolicy:

ExponentialBackOffPolicy: Exponential backoff strategy. Parameters sleeper, initialInterval, maxInterval and multiplier need to be set. initialInterval specifies the initial sleep time, with a default of 100 milliseconds, maxInterval specifies the maximum sleep time, with a default of 30 seconds, and multiplier specifies multiplier, that is, the next sleep time is the current sleep time * multiplier

ExponentialRandomBackOffPolicy: Random exponential backoff strategy, introducing random multiplier can realize random multiplier backoff

FixedBackOffPolicy: Fixed-time backoff strategy. Parameters sleeper and backOffPeriod need to be set. sleeper specifies the waiting strategy. The default is Thread. sleep, that is, the thread sleeps. backOffPeriod specifies the sleep time, and the default is 1 second

NoBackOffPolicy: No backoff algorithm policy, retry immediately every time you retry

UniformRandomBackOffPolicy: Random time backoff strategy. sleeper, minBackOffPeriod and maxBackOffPeriod need to be set. This strategy takes one random sleep time between [minBackOffPeriod and maxBackOffPeriod, and the default time of minBackOffPeriod is 500 milliseconds, and the default time of maxBackOffPeriod is 1500 milliseconds

Application of 3 SpringBoot

1 Import maven coordinates


 <dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.2.RELEASE</version>
 </dependency>

 <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.1</version>
 </dependency>

2 Add 1 management class


@Service
@Slf4j
public class SpringRetryDemo {


    /**
     *  Retry the called method 
     * @return
     */
    // delay=2000L Indicate delay 2 Seconds  multiplier=2 Means double   Namely 1 Second retry 2 Seconds later , No. 1 2 Second retry 4 Seconds later , No. 1 3 Second retry 8 Seconds later 
    @Retryable(value = {RemoteAccessException.class}, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 2))
    public boolean call(Integer param) {
        return RetryDemo.retryMethod(param);
    }

    /**
     *  Exceeds the maximum number of retries or throws an exception that does not specify a retry 
     * @param e
     * @param param
     * @return
     */
    @Recover
    public boolean recover(Exception e, Integer param) {
        log.info(" The request parameter is : ", param);
        log.info(" Exceeds the maximum number of retries or throws an exception that does not specify a retry , e = {} ", e.getMessage());
        return false;
    }
}

3 Add annotation on startup class @ EnableRetry


@SpringBootApplication
@EnableRetry
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}    

4 Adding Test Classes


@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
@Slf4j
public class DemoApplicationTests {

    @Autowired
    private SpringRetryDemo springRetryDemo;

    @Test
    public void testRetry() {
        boolean result = springRetryDemo.call(110);
        log.info(" Method returns a result of : {}", result);
    }
}
/*  Running result :

     Randomly generated number :77
     Greater than 10, Throw a custom exception .
     Randomly generated number :23
     Greater than 10, Throw a custom exception .
     Randomly generated number :82
     Greater than 10, Throw a custom exception .
     The request parameter is : 
     Exceeds the maximum number of retries or throws an exception that does not specify a retry , e =  Greater than 10, Throw a custom exception  
     Method returns a result of : false
*/

Notes:
@ Enableretry annotation, enable retry function (default is based on subclass proxy, default is based on Java interface proxy)


@Slf4j
public class RetryDemo {

    public static boolean retryMethod(Integer param) {
        int i = new Random().nextInt(param);
        log.info(" Randomly generated number :{}", i);

        if (1 == i) {
            log.info(" For 1, Return true.");
            return true;
        } else if (i < 1) {
            log.info(" Less than 1, Throw parameter exception .");
            throw new IllegalArgumentException(" Parameter exception ");
        } else if (i > 1 && i < 10) {
            log.info(" Greater than 1, Less than 10, Throw parameter exception .");
            return false;
        } else {
            // For other 
            log.info(" Greater than 10, Throw a custom exception .");
            throw new RemoteAccessException(" Greater than 10, Throw a custom exception ");
        }
    }
}
0

@ Retryable annotation, marked method retries when an exception occurs

value Specifies an exception to retry include, like value1, defaults to null. When exclude is both null, all exceptions are retried exclude specifies that exceptions are not retried, and the default is null. When include is null at the same time, all exceptions are retried maxAttemps retry times, default 3 The backoff retry supplement mechanism defaults to the @ Backoff () annotation

@Slf4j
public class RetryDemo {

    public static boolean retryMethod(Integer param) {
        int i = new Random().nextInt(param);
        log.info(" Randomly generated number :{}", i);

        if (1 == i) {
            log.info(" For 1, Return true.");
            return true;
        } else if (i < 1) {
            log.info(" Less than 1, Throw parameter exception .");
            throw new IllegalArgumentException(" Parameter exception ");
        } else if (i > 1 && i < 10) {
            log.info(" Greater than 1, Less than 10, Throw parameter exception .");
            return false;
        } else {
            // For other 
            log.info(" Greater than 10, Throw a custom exception .");
            throw new RemoteAccessException(" Greater than 10, Throw a custom exception ");
        }
    }
}
1

@ Backoff notes

How long after delay delay retry Multiple of multiplier delay

@Slf4j
public class RetryDemo {

    public static boolean retryMethod(Integer param) {
        int i = new Random().nextInt(param);
        log.info(" Randomly generated number :{}", i);

        if (1 == i) {
            log.info(" For 1, Return true.");
            return true;
        } else if (i < 1) {
            log.info(" Less than 1, Throw parameter exception .");
            throw new IllegalArgumentException(" Parameter exception ");
        } else if (i > 1 && i < 10) {
            log.info(" Greater than 1, Less than 10, Throw parameter exception .");
            return false;
        } else {
            // For other 
            log.info(" Greater than 10, Throw a custom exception .");
            throw new RemoteAccessException(" Greater than 10, Throw a custom exception ");
        }
    }
}
2

@ Recover notes

When the retry reaches the specified number of times, the annotated marked method will be called, and operations such as logging can be carried out in this method. (The input parameter type and return value type of this method need to be kept as 1 as the retry method)


@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Import(RetryConfiguration.class)
@Documented
public @interface Recover {
}

References:
https://blog.csdn.net/zzzgd_666/article/details/84377962


Related articles: