SpringRetry重试框架的具体使用


Posted in Java/Android onJuly 25, 2021

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重试框架的具体使用

到此这篇关于SpringRetry重试框架的具体使用的文章就介绍到这了,更多相关SpringRetry重试框架内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
springboot @ConfigurationProperties和@PropertySource的区别
Jun 11 Java/Android
浅谈Java实现分布式事务的三种方案
Jun 11 Java/Android
Java输出Hello World完美过程解析
Jun 13 Java/Android
SpringBoot2 参数管理实践之入参出参与校验的方式
Jun 16 Java/Android
深入理解java.lang.String类的不可变性
Jun 27 Java/Android
ObjectMapper 如何忽略字段大小写
Jun 29 Java/Android
Java并发编程必备之Future机制
Jun 30 Java/Android
Java 在线考试云平台的实现
Nov 23 Java/Android
零基础学java之方法的定义与调用详解
Apr 10 Java/Android
Android使用EventBus发送消息,Fragment中接收消息的方法会执行多次
Apr 24 Java/Android
Android开发手册Chip监听及ChipGroup监听
Jun 10 Java/Android
Java实现贪吃蛇游戏的示例代码
Sep 23 Java/Android
Java反应式框架Reactor中的Mono和Flux
关于springboot 配置date字段返回时间戳的问题
Jul 25 #Java/Android
用Java实现简单计算器功能
java设计模式--七大原则详解
java设计模式--建造者模式详解
java设计模式--原型模式详解
SpringBoot快速入门详解
You might like
PHP加Nginx实现动态裁剪图片方案
2014/03/10 PHP
CI框架源码解读之URI.php中_fetch_uri_string()函数用法分析
2016/05/18 PHP
在Thinkphp中使用ajax实现无刷新分页的方法
2016/10/25 PHP
PHP中字符串长度的截取用法示例
2017/01/12 PHP
Jquery 常用方法经典总结
2010/01/28 Javascript
密码强度检测效果实现原理与代码
2013/01/04 Javascript
如何用jquery控制表格奇偶行及活动行颜色
2014/04/20 Javascript
一些老手都不一定知道的JavaScript技巧
2014/05/06 Javascript
jquery中EasyUI使用技巧小结
2015/02/10 Javascript
EasyUI,点击开启编辑框,并且编辑框获得焦点的方法
2015/03/01 Javascript
JavaScript面向对象分层思维全面解析
2016/11/22 Javascript
Jquery与Bootstrap实现后台管理页面增删改查功能示例
2017/01/22 Javascript
canvas绘制环形进度条
2017/02/23 Javascript
JS中关于正则的巧妙操作
2017/08/31 Javascript
iview给radio按钮组件加点击事件的实例
2017/09/30 Javascript
ReactJS实现表单的单选多选和反选的示例
2017/10/13 Javascript
详解vue通过NGINX部署在子目录或者二级目录实践
2018/09/03 Javascript
vuex页面刷新后数据丢失的方法
2019/01/17 Javascript
javascript事件循环event loop的简单模型解释与应用分析
2020/03/14 Javascript
javascript 模块依赖管理的本质深入详解
2020/04/30 Javascript
vue自定义指令和动态路由实现权限控制
2020/08/28 Javascript
[08:44]和酒神一起战斗 DOTA2教你做大人
2014/03/27 DOTA
[04:16]DOTA2全国高校联赛16强抽签
2018/05/02 DOTA
[00:13]天涯墨客二技能展示
2018/08/25 DOTA
[01:45]典藏宝瓶2+祈求者身心——这就是DOTA2TI9总奖金突破3000万美元的秘密
2019/07/21 DOTA
在Python中操作时间之strptime()方法的使用
2020/12/30 Python
Python编程实现双链表,栈,队列及二叉树的方法示例
2017/11/01 Python
django模板结构优化的方法
2019/02/28 Python
python实现简单加密解密机制
2019/03/19 Python
伦敦一卡通:The London Pass
2018/11/30 全球购物
快递业务员岗位职责
2014/01/06 职场文书
酒店总经理职务说明书
2014/02/26 职场文书
简历中自我评价范文
2015/03/11 职场文书
优秀学生干部主要事迹材料
2015/11/04 职场文书
海贼王十大潜力果实,路飞仅排第十,第一可毁世界(震震果实)
2022/03/18 日漫
python模板入门教程之flask Jinja
2022/04/11 Python