SpringRetry重试框架的具体使用

作者:TianXinCoord 时间:2022-09-21 00:37:35 

目录
  • 一、环境搭建

  • 二、RetryTemplate

    • 2.1 RetryTemplate

    • 2.2 RetryListener

    • 2.3 回退策略

      • 2.3.1 FixedBackOffPolicy

      • 2.3.2 ExponentialBackOffPolicy

    • 2.4 重试策略

      • 2.5 RetryCallback

        • 2.6 核心使用

        • 三、EnableRetry

          • 四、Retryable

            spring retry主要实现了重试和熔断。

            不适合重试的场景:

            参数校验不合法、写操作等(要考虑写是否幂等)都不适合重试。

            适合重试的场景:

            远程调用超时、网络突然中断等可以重试。

            在spring retry中可以指定需要重试的异常类型,并设置每次重试的间隔以及如果重试失败是继续重试还是熔断(停止重试)。

            一、环境搭建

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


            <!-- SpringRetry -->
            <dependency>
              <groupId>org.springframework.retry</groupId>
              <artifactId>spring-retry</artifactId>
            </dependency>
            <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-aspects</artifactId>
            </dependency>

            官方文档

            二、RetryTemplate

            2.1 RetryTemplate

            • RetryTemplate封装了Retry基本操作

              • org.springframework.retry.support.RetryTemplate

            • RetryTemplate中可以指定监听、回退策略、重试策略等

            • 只需要正常new RetryTemplate()即可使用

            2.2 RetryListener

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

            org.springframework.retry.RetryListener


            package org.springframework.retry;

            public interface RetryListener {

            /**
             * 任务开始执行时调用,只调用一次
             */
            <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);

            /**
             * 任务执行结束时(包含重试)调用,只调用一次
             */
            <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);

            /**
             * 出现错误时回调
             */
            <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
            }

            配置之后在RetryTemplate中指定

            2.3 回退策略

            2.3.1 FixedBackOffPolicy

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


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

            配置之后在RetryTemplate中指定

            2.3.2 ExponentialBackOffPolicy

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


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

            配置之后在RetryTemplate中指定

            2.4 重试策略

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


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

            配置之后在RetryTemplate中指定

            2.5 RetryCallback

            RetryCallback为retryTemplate.execute时执行的回调


            public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E

            SpringRetry重试框架的具体使用

            2.6 核心使用

            可以使用RetryTemplate完成简单使用
            配置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 {

            /**
                * 注入retryTemplate
                */
               @Bean
               public RetryTemplate retryTemplate() {
                   RetryTemplate retryTemplate = new RetryTemplate();

            /// 回退固定时间(秒)
                  /* FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
                   fixedBackOffPolicy.setBackOffPeriod(1000L);
                   retryTemplate.setBackOffPolicy(fixedBackOffPolicy);*/

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

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

            // 设置 * ,open和close分别在启动和结束时执行一次
                   RetryListener[] listeners = {
                           new RetryListener() {
                               @Override
                               public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                                   PrintUtil.print("open");
                                   return true;
                               }
                               @Override
                               public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
                                       Throwable throwable) {
                                   PrintUtil.print("close");
                               }
                               @Override
                               public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
                                       Throwable throwable) {
                                   PrintUtil.print("onError");
                               }
                           }
                   };
                   retryTemplate.setListeners(listeners);

            return retryTemplate;
               }
            }

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


            @RestController
            public class SpringRetryController {
               @Resource
               private RetryTemplate retryTemplate;
               private static int count = 0;

            @RequestMapping("/retry")
               public Object retry() {
                   try {
                       count = 0;
                       retryTemplate.execute((RetryCallback<Void, RuntimeException>) context -> {
                           // 业务代码
                           // ....
                           // 模拟抛出异常
                           ++count;
                           throw new RuntimeException("抛出异常");
                       });
                   } catch (RuntimeException e) {
                       System.out.println("Exception");
                   }

            return "retry = " + count;
               }
            }

            访问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

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


            import org.springframework.retry.annotation.Retryable;

            public interface RetryService {

            /**
                * 重试方法调用
                */
               @Retryable
               void retryServiceCall();
            }


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

            @EnableRetry
            @Service
            public class RetryServiceImpl implements RetryService {

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

            controller中注入service


            @RequestMapping("/retryAnnotation")
            public Object retryAnnotation() {
               retryService.retryServiceCall();
               return "retryAnnotation";
            }

            将会默认重试

            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

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

            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 {

            /**
             * Retry interceptor bean name to be applied for retryable method. Is mutually
             * exclusive with other attributes.
             * @return the retry interceptor bean name
             */
            String interceptor() default "";

            /**
             * 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
             */
            Class<? extends Throwable>[] value() default {};

            /**
             * Exception types that are retryable. Defaults to empty (and if excludes is also
             * empty all exceptions are retried).
             * @return exception types to retry
             */
            Class<? extends Throwable>[] include() default {};

            /**
             * 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
             */
            Class<? extends Throwable>[] exclude() default {};

            /**
             * 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
             */
            String label() default "";

            /**
             * 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
             */
            boolean stateful() default false;

            /**
             * @return the maximum number of attempts (including the first failure), defaults to 3
             */
            int maxAttempts() default 3;

            /**
             * @return an expression evaluated to the maximum number of attempts (including the first failure), defaults to 3
             * Overrides {@link #maxAttempts()}.
             * @date 1.2
             */
            String maxAttemptsExpression() default "";

            /**
             * 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
             */
            Backoff backoff() default @Backoff();

            /**
             * 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
             */
            String exceptionExpression() default "";

            /**
             * Bean names of retry listeners to use instead of default ones defined in Spring context
             * @return retry listeners bean names
             */
            String[] listeners() default {};

            }

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

            /**
             * Synonym for {@link #delay()}.
             *
             * @return the delay in milliseconds (default 1000)
             */
            long value() default 1000;

            /**
             * 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)
             */
            long delay() default 0;

            /**
             * 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)
             */
            long maxDelay() default 0;

            /**
             * 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)
             */
            double multiplier() default 0;

            /**
             * 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
             */
            String delayExpression() default "";

            /**
             * 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
             */
            String maxDelayExpression() default "";

            /**
             * 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
             */
            String multiplierExpression() default "";

            /**
             * In the exponential case ({@link #multiplier()} &gt; 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)
             */
            boolean random() default false;
            }

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


            @Component
            public class PlatformClassService {
               @Retryable(
                   // 重试异常的异常类型
                   value = {Exception.class},
                   // 最大重试次数
                   maxAttempts = 5,
                   // 设置回退延迟时间
                   backoff = @Backoff(delay = 500),
                   // 配置回调方法名称
                   listeners = "retryListener"
               )
               public void call() {
                   System.out.println("call...");
                   throw new RuntimeException("手工异常");
               }
            }

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

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


            /**
             * 注解调用
             */
            @Bean
            public RetryListener retryListener() {
               return new RetryListener() {
                   @Override
                   public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                       System.out.println("open context = " + context + ", callback = " + callback);
                       // 返回true继续执行后续调用
                       return true;
                   }

            @Override
                   public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
                                                              Throwable throwable) {
                       System.out.println("close context = " + context + ", callback = " + callback);
                   }
                   @Override
                   public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
                                                                Throwable throwable) {
                       System.out.println("onError context = " + context + ", callback = " + callback);
                   }
               };
            }

            调用服务


            @RestController
            public class SpringRetryController {
               @Resource
               private PlatformClassService platformClassService;

            @RequestMapping("/retryPlatformCall")
               public Object retryPlatformCall() {
                   try {
                       platformClassService.call();
                   } catch (Exception e) {
                       return "尝试调用失败";
                   }
                   return "retryPlatformCall";
               }
            }

            调用结果

            SpringRetry重试框架的具体使用

            来源:https://juejin.cn/post/6988384668500361229

            标签:SpringRetry,重试
            0
            投稿

            猜你喜欢

          • Spring5路径匹配器PathPattern解析

            2021-07-09 00:12:28
          • java字符串抉择

            2023-08-08 14:20:05
          • Android实现简易计步器功能隔天步数清零查看历史运动纪录

            2021-07-24 00:37:02
          • Android Studio实现简易计算器(表格布局TableLayout)

            2021-11-17 23:20:07
          • C++实现哈夫曼树算法

            2023-05-21 01:03:46
          • Android实现购物车及其他功能的角标

            2021-12-24 10:54:54
          • c# 网络编程之tcp

            2022-07-24 03:27:27
          • Android侧滑效果简单实现代码

            2023-02-25 09:50:00
          • Java实现中英文词典功能

            2021-06-20 18:25:56
          • Android自定义View图片按Path运动和旋转

            2022-09-15 22:53:11
          • Java中的权限修饰符(protected)示例详解

            2023-04-16 10:23:24
          • C语言二叉树常见操作详解【前序,中序,后序,层次遍历及非递归查找,统计个数,比较,求深度】

            2021-09-30 08:41:23
          • c# 曲线图生成代码

            2023-03-27 07:52:55
          • EasyValidate优雅地校验提交数据完整性

            2022-03-30 11:54:46
          • android 调用系统的照相机和图库实例详解

            2022-01-02 11:46:47
          • java 二分法算法的实例

            2023-04-25 05:04:05
          • Jenkins使用Gradle编译Android项目详解

            2021-12-30 22:26:30
          • 退出Android程序时清除所有activity的实现方法

            2021-10-09 23:49:30
          • java判断http地址是否连通(示例代码)

            2023-08-05 03:24:05
          • Android学习笔记之蓝牙功能

            2022-05-19 05:07:44
          • asp之家 软件编程 m.aspxhome.com