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的Hibernate框架中的继承映射学习教程

    Java的Hibernate框架中的继承映射学习教程

    Hibernate中的映射可以将类与表对应,并利用类的继承特性,这里我们就来看一下Java的Hibernate框架中的继承映射学习教程
    2016-07-07
  • java中获取xml文件的某个配置节点内容方式

    java中获取xml文件的某个配置节点内容方式

    这篇文章主要介绍了java中获取xml文件的某个配置节点内容方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • Spring Boot中@Validated注解不生效问题汇总大全

    Spring Boot中@Validated注解不生效问题汇总大全

    这篇文章主要给大家介绍了关于Spring Boot中@Validated注解不生效问题汇总的相关资料,@Validated注解是Spring框架中的一个注解,用于在方法参数上添加参数校验规则,需要的朋友可以参考下
    2023-07-07
  • Spring扩展之基于HandlerMapping实现接口灰度发布实例

    Spring扩展之基于HandlerMapping实现接口灰度发布实例

    这篇文章主要介绍了Spring扩展之基于HandlerMapping实现接口灰度发布实例,灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式,灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度,需要的朋友可以参考下
    2023-08-08
  • Java运算符的常见问题与用法小结

    Java运算符的常见问题与用法小结

    这篇文章主要介绍了Java运算符,结合实例形式总结分析了Java各种常见运算符,包括算术运算符、比较运算符、逻辑运算符、位运算符等相关功能、原理与使用技巧,需要的朋友可以参考下
    2020-04-04
  • java项目实现图片等比缩放

    java项目实现图片等比缩放

    这篇文章主要为大家详细介绍了java项目实现图片等比缩放,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 详解Java中Thread 和Runnable区别

    详解Java中Thread 和Runnable区别

    这篇文章主要介绍了Java中Thread 和Runnable的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • 分析JVM源码之Thread.interrupt系统级别线程打断

    分析JVM源码之Thread.interrupt系统级别线程打断

    在java编程中,我们经常会调用Thread.sleep()方法使得线程停止运行一段时间,而Thread类中也提供了interrupt方法供我们去主动打断一个线程。那么线程挂起和打断的本质究竟是什么,本文就此问题作一个探究
    2021-06-06
  • java获取反射机制的3种方法总结

    java获取反射机制的3种方法总结

    这篇文章主要给大家介绍了关于java获取反射机制的3种方法,文中通过示例代码介绍的非常详细,对大家学习或者使用java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • SpringBoot向容器注册bean的方法详解

    SpringBoot向容器注册bean的方法详解

    这篇文章主要利用示例为大家详细介绍了SpringBoot如何向容器注册bean(即:将对象加入容器)的四种方法,文中的示例代码讲解详细,需要的可以参考一下
    2022-05-05

最新评论