SpringBoot技术实践-SpringRetry重试框架

冷不防 2022-08-31 09:59 350阅读 0赞

一、环境搭建

  1. 加入SpringRetry依赖,SpringRetry使用AOP实现,所以也需要加入AOP包


    org.springframework.retry
    spring-retry


    org.springframework
    spring-aspects
  2. 官方文档

    • https://www.baeldung.com/spring-retry

二、RetryTemplate

2.1 RetryTemplate

  1. RetryTemplate封装了Retry基本操作

    • org.springframework.retry.support.RetryTemplate
  2. RetryTemplate中可以指定监听、回退策略、重试策略等
  3. 只需要正常new RetryTemplate()即可使用

2.2 RetryListener

  1. RetryListener指定了当执行过程中出现错误时的回调

    • org.springframework.retry.RetryListener

    package org.springframework.retry;

    public interface RetryListener {

    1. /** * 任务开始执行时调用,只调用一次 */
    2. <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);
    3. /** * 任务执行结束时(包含重试)调用,只调用一次 */
    4. <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
    5. /** * 出现错误时回调 */
    6. <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);

    }

  2. 配置之后在RetryTemplate中指定

2.3 回退策略

2.3.1 FixedBackOffPolicy

  1. 当出现错误时延迟多少时间继续调用

    FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
    fixedBackOffPolicy.setBackOffPeriod(1000L);
    retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

  2. 配置之后在RetryTemplate中指定

2.3.2 ExponentialBackOffPolicy

  1. 当出现错误时第一次按照指定延迟时间延迟后按照指数进行延迟

    // 指数回退(秒),第一次回退1s,第二次回退2s,第三次4秒,第四次8秒
    ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
    exponentialBackOffPolicy.setInitialInterval(1000L);
    exponentialBackOffPolicy.setMultiplier(2);
    retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);

  2. 配置之后在RetryTemplate中指定

2.4 重试策略

  1. 重试策略主要指定出现错误时重试次数

    // 重试策略
    SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
    retryPolicy.setMaxAttempts(5);
    retryTemplate.setRetryPolicy(retryPolicy);

  2. 配置之后在RetryTemplate中指定

2.5 RetryCallback

  1. RetryCallback为retryTemplate.execute时执行的回调

    • public final T execute(RetryCallback retryCallback) throws E

image.png

2.6 核心使用

  1. 可以使用RetryTemplate完成简单使用
  2. 配置retryTemplate

    • 指定回退策略为ExponentialBackOffPolicy
    • 指定重试策略为SimpleRetryPolicy
    • 指定监听器RetryListener

    import com.codecoord.util.PrintUtil;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.retry.RetryCallback;
    import org.springframework.retry.RetryContext;
    import org.springframework.retry.RetryListener;
    import org.springframework.retry.backoff.ExponentialBackOffPolicy;
    import org.springframework.retry.policy.SimpleRetryPolicy;
    import org.springframework.retry.support.RetryTemplate;

    @Configuration
    public class RetryTemplateConfig {

    1. /** * 注入retryTemplate */
    2. @Bean
    3. public RetryTemplate retryTemplate() {
    4. RetryTemplate retryTemplate = new RetryTemplate();
    5. /// 回退固定时间(秒)
    6. /* FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(1000L); retryTemplate.setBackOffPolicy(fixedBackOffPolicy);*/
    7. // 指数回退(秒),第一次回退1s,第二次回退2s
    8. ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
    9. exponentialBackOffPolicy.setInitialInterval(1000L);
    10. exponentialBackOffPolicy.setMultiplier(2);
    11. retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);
    12. // 重试策略
    13. SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
    14. retryPolicy.setMaxAttempts(5);
    15. retryTemplate.setRetryPolicy(retryPolicy);
    16. // 设置监听器,open和close分别在启动和结束时执行一次
    17. RetryListener[] listeners = {
    18. new RetryListener() {
    19. @Override
    20. public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
    21. PrintUtil.print("open");
    22. return true;
    23. }
    24. @Override
    25. public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
    26. Throwable throwable) {
    27. PrintUtil.print("close");
    28. }
    29. @Override
    30. public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
    31. Throwable throwable) {
    32. PrintUtil.print("onError");
    33. }
    34. }
    35. };
    36. retryTemplate.setListeners(listeners);
    37. return retryTemplate;
    38. }

    }

  3. 在controller中注入RetryTemplate使用,也可以是在service中

    @RestController
    public class SpringRetryController {

    1. @Resource
    2. private RetryTemplate retryTemplate;
    3. private static int count = 0;
    4. @RequestMapping("/retry")
    5. public Object retry() {
    6. try {
    7. count = 0;
    8. retryTemplate.execute((RetryCallback<Void, RuntimeException>) context -> {
    9. // 业务代码
    10. // ....
    11. // 模拟抛出异常
    12. ++count;
    13. throw new RuntimeException("抛出异常");
    14. });
    15. } catch (RuntimeException e) {
    16. System.out.println("Exception");
    17. }
    18. return "retry = " + count;
    19. }

    }

  4. 访问retry接口,然后观察日志输出

    18:27:20.648 - http-nio-8888-exec-1 - open
    18:27:20.649 - http-nio-8888-exec-1 - retryTemplate.execute执行
    18:27:20.649 - http-nio-8888-exec-1 - onError
    18:27:21.658 - http-nio-8888-exec-1 - retryTemplate.execute执行
    18:27:21.658 - http-nio-8888-exec-1 - onError
    18:27:23.670 - http-nio-8888-exec-1 - retryTemplate.execute执行
    18:27:23.670 - http-nio-8888-exec-1 - onError
    18:27:27.679 - http-nio-8888-exec-1 - retryTemplate.execute执行
    18:27:27.679 - http-nio-8888-exec-1 - onError
    18:27:35.681 - http-nio-8888-exec-1 - retryTemplate.execute执行
    18:27:35.681 - http-nio-8888-exec-1 - onError
    18:27:35.681 - http-nio-8888-exec-1 - close

三、EnableRetry

  1. @EnableRetry开启重试,在类上指定的时候方法将默认执行,重试三次
  2. 定义service,开启@EnableRetry注解和指定@Retryable,重试可以参考后面一节

    import org.springframework.retry.annotation.Retryable;

    public interface RetryService {

    1. /** * 重试方法调用 */
    2. @Retryable
    3. void retryServiceCall();

    }

    import org.springframework.retry.annotation.EnableRetry;
    import org.springframework.stereotype.Service;

    @EnableRetry
    @Service
    public class RetryServiceImpl implements RetryService {

    1. @Override
    2. public void retryServiceCall() {
    3. PrintUtil.print("方法调用..");
    4. throw new RuntimeException("手工异常");
    5. }

    }

  3. controller中注入service

    @RequestMapping(“/retryAnnotation”)
    public Object retryAnnotation() {

    1. retryService.retryServiceCall();
    2. return "retryAnnotation";

    }

  4. 将会默认重试

    18:46:48.721 - http-nio-8888-exec-1 - 方法调用..
    18:46:49.724 - http-nio-8888-exec-1 - 方法调用..
    18:46:50.730 - http-nio-8888-exec-1 - 方法调用..
    java.lang.RuntimeException: 手工异常

四、Retryable

  1. 用于需要重试的方法上的注解
  2. 有以下几个属性

    • Retryable注解参数

      • value:指定发生的异常进行重试
      • include:和value一样,默认空,当exclude也为空时,所有异常都重试
      • exclude:指定异常不重试,默认空,当include也为空时,所有异常都重试
      • maxAttemps:重试次数,默认3
      • backoff:重试补偿机制,默认没有
    • @Backoff 注解 重试补偿策略

      • 不设置参数时,默认使用FixedBackOffPolicy(指定等待时间),重试等待1000ms
      • 设置delay,使用FixedBackOffPolicy(指定等待设置delay和maxDealy时,重试等待在这两个值之间均态分布)
      • 设置delay、maxDealy、multiplier,使用 ExponentialBackOffPolicy(指数级重试间隔的实现),multiplier即指定延迟倍数,比如delay=5000L,multiplier=2,则第一次重试为5秒,第二次为10秒,第三次为20秒

    @Target({ ElementType.METHOD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Retryable {

    1. /** * Retry interceptor bean name to be applied for retryable method. Is mutually * exclusive with other attributes. * @return the retry interceptor bean name */
    2. String interceptor() default "";
    3. /** * Exception types that are retryable. Synonym for includes(). Defaults to empty (and * if excludes is also empty all exceptions are retried). * @return exception types to retry */
    4. Class<? extends Throwable>[] value() default { };
    5. /** * Exception types that are retryable. Defaults to empty (and if excludes is also * empty all exceptions are retried). * @return exception types to retry */
    6. Class<? extends Throwable>[] include() default { };
    7. /** * Exception types that are not retryable. Defaults to empty (and if includes is also * empty all exceptions are retried). * If includes is empty but excludes is not, all not excluded exceptions are retried * @return exception types not to retry */
    8. Class<? extends Throwable>[] exclude() default { };
    9. /** * A unique label for statistics reporting. If not provided the caller may choose to * ignore it, or provide a default. * * @return the label for the statistics */
    10. String label() default "";
    11. /** * Flag to say that the retry is stateful: i.e. exceptions are re-thrown, but the * retry policy is applied with the same policy to subsequent invocations with the * same arguments. If false then retryable exceptions are not re-thrown. * @return true if retry is stateful, default false */
    12. boolean stateful() default false;
    13. /** * @return the maximum number of attempts (including the first failure), defaults to 3 */
    14. int maxAttempts() default 3;
    15. /** * @return an expression evaluated to the maximum number of attempts (including the first failure), defaults to 3 * Overrides {@link #maxAttempts()}. * @date 1.2 */
    16. String maxAttemptsExpression() default "";
    17. /** * Specify the backoff properties for retrying this operation. The default is a * simple {@link Backoff} specification with no properties - see it's documentation * for defaults. * @return a backoff specification */
    18. Backoff backoff() default @Backoff();
    19. /** * Specify an expression to be evaluated after the {@code SimpleRetryPolicy.canRetry()} * returns true - can be used to conditionally suppress the retry. Only invoked after * an exception is thrown. The root object for the evaluation is the last {@code Throwable}. * Other beans in the context can be referenced. * For example: * <pre class=code> * {@code "message.contains('you can retry this')"}. * </pre> * and * <pre class=code> * {@code "@someBean.shouldRetry(#root)"}. * </pre> * @return the expression. * @date 1.2 */
    20. String exceptionExpression() default "";
    21. /** * Bean names of retry listeners to use instead of default ones defined in Spring context * @return retry listeners bean names */
    22. String[] listeners() default { };

    }

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Backoff {

    1. /** * Synonym for {@link #delay()}. * * @return the delay in milliseconds (default 1000) */
    2. long value() default 1000;
    3. /** * A canonical backoff period. Used as an initial value in the exponential case, and * as a minimum value in the uniform case. * @return the initial or canonical backoff period in milliseconds (default 1000) */
    4. long delay() default 0;
    5. /** * The maximimum wait (in milliseconds) between retries. If less than the * {@link #delay()} then the default of * {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL} * is applied. * * @return the maximum delay between retries (default 0 = ignored) */
    6. long maxDelay() default 0;
    7. /** * If positive, then used as a multiplier for generating the next delay for backoff. * * @return a multiplier to use to calculate the next backoff delay (default 0 = * ignored) */
    8. double multiplier() default 0;
    9. /** * An expression evaluating to the canonical backoff period. Used as an initial value * in the exponential case, and as a minimum value in the uniform case. Overrides * {@link #delay()}. * @return the initial or canonical backoff period in milliseconds. * @date 1.2 */
    10. String delayExpression() default "";
    11. /** * An expression evaluating to the maximimum wait (in milliseconds) between retries. * If less than the {@link #delay()} then the default of * {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL} * is applied. Overrides {@link #maxDelay()} * * @return the maximum delay between retries (default 0 = ignored) * @date 1.2 */
    12. String maxDelayExpression() default "";
    13. /** * Evaluates to a vaule used as a multiplier for generating the next delay for * backoff. Overrides {@link #multiplier()}. * * @return a multiplier expression to use to calculate the next backoff delay (default * 0 = ignored) * @date 1.2 */
    14. String multiplierExpression() default "";
    15. /** * In the exponential case ({@link #multiplier()} > 0) set this to true to have the * backoff delays randomized, so that the maximum delay is multiplier times the * previous delay and the distribution is uniform between the two values. * * @return the flag to signal randomization is required (default false) */
    16. boolean random() default false;

    }

  3. 在需要重试的方法上配置对应的重试次数、重试异常的异常类型、设置回退延迟时间、重试策略、方法监听名称

    @Component
    public class PlatformClassService {

    1. @Retryable(
    2. // 重试异常的异常类型
    3. value = { Exception.class},
    4. // 最大重试次数
    5. maxAttempts = 5,
    6. // 设置回退延迟时间
    7. backoff = @Backoff(delay = 500),
    8. // 配置回调方法名称
    9. listeners = "retryListener"
    10. )
    11. public void call() {
    12. System.out.println("call...");
    13. throw new RuntimeException("手工异常");
    14. }

    }

    // 初始延迟2秒,然后之后验收1.5倍延迟重试,总重试次数4
    @Retryable(value = { Exception.class}, maxAttempts = 4, backoff = @Backoff(delay = 2000L, multiplier = 1.5))

  4. 监听方法,在配置类中进行配置

    /* 注解调用 */
    @Bean
    public RetryListener retryListener() {

    1. return new RetryListener() {
    2. @Override
    3. public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
    4. System.out.println("open context = " + context + ", callback = " + callback);
    5. // 返回true继续执行后续调用
    6. return true;
    7. }
    8. @Override
    9. public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
    10. Throwable throwable) {
    11. System.out.println("close context = " + context + ", callback = " + callback);
    12. }
    13. @Override
    14. public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
    15. Throwable throwable) {
    16. System.out.println("onError context = " + context + ", callback = " + callback);
    17. }
    18. };

    }

  5. 调用服务

    @RestController
    public class SpringRetryController {

    1. @Resource
    2. private PlatformClassService platformClassService;
    3. @RequestMapping("/retryPlatformCall")
    4. public Object retryPlatformCall() {
    5. try {
    6. platformClassService.call();
    7. } catch (Exception e) {
    8. return "尝试调用失败";
    9. }
    10. return "retryPlatformCall";
    11. }

    }

  6. 调用结果

image.png

发表评论

表情:
评论列表 (有 0 条评论,350人围观)

还没有评论,来说两句吧...

相关阅读