Spring中的接口重试机制解析

 更新时间:2024年01月24日 08:32:07   作者:北漂码农有话说  
这篇文章主要介绍了Spring中的接口重试机制解析,大家在做项目的时候,往往会遇到一些接口由于网络抖动等问题导致接口响应超时等,这时候我们会希望能够按照一定的规则进行接口请求重试,需要的朋友可以参考下

背景

大家在做项目的时候,往往会遇到一些接口由于网络抖动等问题导致接口响应超时等,这时候我们会希望能够按照一定的规则进行接口 请求重试。

分析

一般情况下,以上描述的情况,我们可能需要后台的定时任务去重新发起调用,以达到目的,这样无疑会增加开发成本,并且还得考虑 请求报文的保存等等问题。

这时我们可以使用Spring提供的功能来完成这个需求。

实现

假设我们现在有一个接口,在这个接口流程中会出现一些异常,比如超时异常(数据库的异常、远程调用的异常等),出现这样的异常 我们就希望能够自动发起重新调用的功能。

创建工程

我们为了测试方便就直接创建一个简单的Spring Boot的工程就可以了。创建的时候引入如下依赖:

<dependency>
   <groupId>org.springframework.retry</groupId>
   <artifactId>spring-retry</artifactId>
</dependency>
<!--AOP依赖-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

说明:由于Spring重试机制的基于注解的AOP实现,所以我们需要映入AOP的依赖,我们是Spring Boot项目直接使用AOP的启动器就可以了。

启动注解

由于我们是继续Spring Boot来开发这部分代码,所以我们需要配置开启重试机制的的注解,代码如下:

@SpringBootApplication
@EnableRetry
public class SpringbootApplication {
 public static void main(String[] args) {
  SpringApplication.run(SpringbootApplication.class, args);
 }
}

说明:Spring重试机制主要是使用注解@Retryable来实现的

@Retryable

该注解的源码如下:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {
    String recover() default "";
    String interceptor() default "";
    Class<? extends Throwable>[] value() default {};
    Class<? extends Throwable>[] include() default {};
    Class<? extends Throwable>[] exclude() default {};
    String label() default "";
    boolean stateful() default false;
    int maxAttempts() default 3;
    String maxAttemptsExpression() default "";
    Backoff backoff() default @Backoff;
    String exceptionExpression() default "";
    String[] listeners() default {};
}

说明:

  •  value:抛出指定异常才会重试
  •  include:和value一样,默认为空,当exclude也为空时,默认所有异常
  •  exclude:指定不处理的异常
  •  maxAttempts:最大重试次数,默认3次
  •  backoff:重试等待策略, 默认使用@Backoff,@Backoff的value默认为1000, 以毫秒为单位的延迟(默认 1000)
  •  multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。

注解@Retryable切面类

该注解主要的切面拦截器如下代码,感兴趣的小伙伴可以自行查看源码,分析。

AnnotationAwareRetryOperationsInterceptor#invoke()

@Recover

Spring-Retry还提供了@Recover注解,用于@Retryable重试失败后处理方法。如果不需要回调方法,可以直接不写回调方法,那么实现的效果是,重试次数完了后,如果还是没成功没符合业务判断,就抛出异常。 可以看到传参里面写的是Exception e,这个是作为回调的标识(重试次数用完了,还是失败,我们抛出这个Exception e通知触发这个回调方法)。

注意事项:

1、方法的返回值必须与@Retryable方法一致 

2、方法的第一个参数,必须是Throwable类型的,建议是与@Retryable配置的异常一致,其他的参数,需要哪个参数,写进去就可以了(@Recover方法中有的) 

3、该回调方法与重试方法写在同一个实现类里面 

4、由于是基于AOP实现,所以不支持类里自调用方法 

5、如果重试失败需要给@Recover注解的方法做后续处理,那这个重试的方法不能有返回值,只能是void 

6、方法内不能使用try catch,只能往外抛异常 

7、@Recover注解来开启重试失败后调用的方法(注意,需跟重处理方法在同一个类中),此注解注释的方法参数一定要是@Retryable抛出的异常。

该注解的代码如下:

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

注解@Recover切面类

AnnotationAwareRetryOperationsInterceptor#getRecoverer()

写一个Demo 创建接口

首先我们创建一个接口。代码如下:

public interface RetryService {
    /**
     * 重试方法
     * @param str
     * @return
     * @throws Exception
     */
    String retry(String str) throws Exception;
    /**
     * 回调方法
     * @param e
     * @param str
     * @return
     */
    String recover(Exception e,String str);
}

接口实现

上述接口的实现的代码如下:

@Service
@Slf4j
public class RetryServiceImpl implements RetryService {
    /**
     * 重试方法
     * @param str
     * @return
     */
    @Override
    @Retryable(value = Exception.class,maxAttempts = 5,backoff = @Backoff(delay = 2000,multiplier = 1.5))
    public String retry(String str) throws Exception {
        log.info("Service 请求入参为:{}",str);
        log.info("进入测试方法,目前时间为:{}",new Date());
        if ("succ".equals(str)){
            return "succ";
        }else {
            throw new Exception("异常了!");
        }
    }
    /**
     * 重试次数完成后,回调的方法
     * @param e
     * @param str
     * @return
     */
    @Override
    @Recover
    public String recover(Exception e, String str) {
        log.info("异常出现后的回调操作,入参为:{},当前时间为:{}", str,LocalDate.now());
        return null;
    }
}

测试

我们使用postman进行测试

创建测试类

我们创建一个控制器来测试功能。代码如下:

@RestController
@Slf4j
public class RetryController {
    @Resource
    RetryService retryService;
    @GetMapping("/re")
    public String retry(@RequestParam("str") String str) throws Exception{
        log.info("Controller 请求入参为:{}",str);
        return retryService.retry(str);
    }
}

说明:我们的入参是一个字符串,若传入的字符串非succ那么手动抛出异常,我们观察日志,看是否框架自动发起了重试。

测试

我们启动Sping Boot项目,并且进行测试,来看一下效果。

使用postman请求我们的login方法,我们观察一下日志。请求地址localhost:8111/re,日志如下:

2023-07-17 21:05:19.488  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.controller.RetryController       : Controller 请求入参为:111
2023-07-17 21:05:19.516  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : Service 请求入参为:111
2023-07-17 21:05:19.516  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : 进入测试方法,目前时间为:Mon Jul 17 21:05:19 CST 2023
2023-07-17 21:05:21.518  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : Service 请求入参为:111
2023-07-17 21:05:21.519  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : 进入测试方法,目前时间为:Mon Jul 17 21:05:21 CST 2023
2023-07-17 21:05:24.522  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : Service 请求入参为:111
2023-07-17 21:05:24.523  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : 进入测试方法,目前时间为:Mon Jul 17 21:05:24 CST 2023
2023-07-17 21:05:29.024  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : Service 请求入参为:111
2023-07-17 21:05:29.025  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : 进入测试方法,目前时间为:Mon Jul 17 21:05:29 CST 2023
2023-07-17 21:05:35.779  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : Service 请求入参为:111
2023-07-17 21:05:35.779  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : 进入测试方法,目前时间为:Mon Jul 17 21:05:35 CST 2023
2023-07-17 21:05:35.790  INFO 2007 --- [nio-8111-exec-2] o.t.s.l.service.impl.RetryServiceImpl    : 异常出现后的回调操作,入参为:111,当前时间为:2023-07-17

可以看到,出现异常后框架自动发起了重试,在重试次数使用完成后,回调了异常处理的方法。

到此这篇关于Spring中的接口重试机制解析的文章就介绍到这了,更多相关Spring接口重试内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java 异常捕获及处理案例详解

    java 异常捕获及处理案例详解

    这篇文章主要介绍了java 异常捕获及处理案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-09-09
  • Java通过MyBatis框架对MySQL数据进行增删查改的基本方法

    Java通过MyBatis框架对MySQL数据进行增删查改的基本方法

    MyBatis框架由Java的JDBC API进一步封装而来,在操作数据库方面效果拔群,接下来我们就一起来看看Java通过MyBatis框架对MySQL数据进行增删查改的基本方法:
    2016-06-06
  • java数据结构-堆实现优先队列

    java数据结构-堆实现优先队列

    通常都把队列比喻成排队买东西,大家都很守秩序,先排队的人就先买东西。但是优先队列有所不同,它不遵循先进先出的规则,而是根据队列中元素的优先权,优先权最大的先被取出,这篇文章主要介绍了java数据结构-堆实现优先队列,感兴趣的朋友一起看看吧
    2021-08-08
  • java Bean与json对象间的转换实例讲解

    java Bean与json对象间的转换实例讲解

    在本篇文章里小编给大家整理的是关于java Bean与json间的转换的实例内容,有需要的朋友们吧可以学习参考下。
    2020-01-01
  • 图文并茂讲解RocketMQ消息类别

    图文并茂讲解RocketMQ消息类别

    这篇文章主要介绍了图文并茂讲解RocketMQ消息类别,RocketMQ对于消息提供了很多用法,包括:同步消息、异步消息、单向发送、顺序消息、延时消息、批量消息、过滤消息、事务消息等
    2022-12-12
  • Mybatis Generator逆向工程的使用详细教程

    Mybatis Generator逆向工程的使用详细教程

    这篇文章主要介绍了Mybatis Generator逆向工程的使用详细教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • IDEA中make directory as的作用及说明

    IDEA中make directory as的作用及说明

    这篇文章主要介绍了IDEA中make directory as的作用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-09-09
  • Spring Cloud Gateway 服务网关的部署与使用详细讲解

    Spring Cloud Gateway 服务网关的部署与使用详细讲解

    这篇文章主要介绍了Spring Cloud Gateway 服务网关的部署与使用详细介绍,本文给大家讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • Spring createBeanInstance实例化Bean

    Spring createBeanInstance实例化Bean

    这篇文章主要为大家介绍了Spring createBeanInstance实例化Bean源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • JAVA抽象类及接口使用方法解析

    JAVA抽象类及接口使用方法解析

    这篇文章主要介绍了JAVA抽象类及接口使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08

最新评论