• 5分钟攻略Spring-Retry框架实现经典重试场景


    前言

    今天分享干货,控制了篇幅,5分钟内就能看完学会。

    主题是Spring-Retry框架的应用,做了一个很清晰的案例,代码可下载自测。

    框架介绍

    Spring-Retry框架是Spring自带的功能,具备间隔重试包含异常排除异常控制重试频率等特点,是项目开发中很实用的一种框架。

    本篇所用框架的版本如下:

    技术 版本
    Java 17
    SpringBoot 3.2
    Spring-retry 2.0.4

    正文

    1、引入依赖

    坑点:需要引入AOP,否则会抛异常。

    
    <dependency>
        <groupId>org.springframework.retrygroupId>
        <artifactId>spring-retryartifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-aopartifactId>
    dependency>
    

    2、启动类注解

    坑点:很容易一时疏忽忘记启动类开启@EnableRetry,大家别忘了哦。

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.retry.annotation.EnableRetry;
    
    @SpringBootApplication
    @EnableRetry
    public class SpringRetryDemoApplication {
    
       public static void main(String[] args) {
          SpringApplication.run(SpringRetryDemoApplication.class, args);
       }
    
    }
    

    3、模拟发短信

    我们模拟一个发短信功能,根据随机数分别作为成功、失败、抛出各种异常的入口。

    这里抛出几种异常的目的,是为了后面演示出重试注解参数产生的效果。

    import cn.hutool.core.util.RandomUtil;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 

    * 短信服务工具类 *

    * * @author 程序员济癫 * @since 2023-12-21 09:40 */
    @Slf4j public class SmsUtil { /** * 发送短信 */ public static boolean sendSms() { // 使用随机数模拟重试场景 int num = RandomUtil.randomInt(4); log.info("[SmsUtil][sendSms]>>>> random num = {}", num); return switch (num) { case 0 -> // 模拟发生参数异常 throw new IllegalArgumentException("参数有误!"); case 1 -> // 模拟发生数组越界异常 throw new ArrayIndexOutOfBoundsException("数组越界!"); case 2 -> // 模拟成功 true; case 3 -> // 模拟发生空指针界异常 throw new NullPointerException(); default -> // 未成功则返回false false; }; } }

    4、Retry应用

    我们单独写一个用于重试调用的组件类,用于业务类调用。

    import com.example.springretrydemo.util.SmsUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.retry.annotation.Backoff;
    import org.springframework.retry.annotation.Recover;
    import org.springframework.retry.annotation.Retryable;
    import org.springframework.stereotype.Component;
    
    import java.time.LocalDateTime;
    import java.time.format.DateTimeFormatter;
    
    /**
     * 

    * 重试组件 *

    * * @author 程序员济癫 * @since 2023-12-21 09:43 */
    @Slf4j @Component public class RetryComponent { /** * 重试机制发送短信 */ @Retryable( retryFor = {IllegalArgumentException.class, ArrayIndexOutOfBoundsException.class}, noRetryFor = {NullPointerException.class}, maxAttempts = 4, backoff = @Backoff(delay = 2000L, multiplier = 2) ) public boolean sendSmsRetry() { log.info("[RetryComponent][sendSmsRetry]>>>> 当前时间:{}", getNowTime()); return SmsUtil.sendSms(); } /** * 兜底方法,规则: * 1、超出了最大重试次数; * 2、抛出了不进行重试的异常; */ @Recover public boolean recover() { log.info("[RetryComponent][recover]>>>> 短信发送次数过多,请稍后重试!"); return false; } /** * 获取当前时间 */ private String getNowTime() { return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } }

    @Retryable注解参数说明:

    • retryFor:此参数包含的异常会触发重试机制,多个异常则以数组形式定义。
    • noRetryFor:此参数包含的异常不会触发重试机制,多个异常则以数组形式定义。
    • maxAttempts:重试最大次数,不定义则默认3次。
    • backoff:定义补偿机制,delay-延迟时间(s),multiplier-重试时间的倍数(比如设置为2,重试4次的话,补偿机制就是分别间隔2s、4s、8s做重试)

    @Recover注解说明:用于兜底,当 超出了最大重试次数抛出了不进行重试的异常 时,直接执行该注解声明的兜底方法。

    PS:顺便提一句,如果是 SpringBoot2.x 的版本,这里@Retryable注解的retryFor参数对应的是includenoRetryFor参数对应的是exclude,可以直接点进去看源码便一目了然。


    5、JunitTest测试

    我们编写一个Junit测试类来测试重试的效果,并打印出结果信息。

    import com.example.springretrydemo.retry.RetryComponent;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @Slf4j
    @SpringBootTest
    class SpringRetryDemoApplicationTests {
    
       @Autowired
       private RetryComponent retryComponent;
    
       @Test
       void sendSmsTest() {
          boolean ret = retryComponent.sendSmsRetry();
          log.info("sendSmsTest result = {}", ret);
       }
    
    }
    

    6、效果

    第1次测试时,可以看到,随机数刚好都是1,走的是数组越界异常。

    而这个异常在retryFor中定义了,所以执行了4次,直到结束,最后进入了兜底方法。

    同时,可以看到,执行4次的频率也和预想一样是2s、4s、8s。

    image

    第2次测试时,可以看到,随机数是3,走的是空指针异常。

    而这个异常在noRetryFor中定义了,所以接下来直接进入了兜底方法。

    image

    第3次测试时,可以看到,第一次随机数是0,走的参数异常,在retryFor中,所以2s后继续重试。

    然后随机数是2,表示业务成功,所以直接返回了true。

    这个场景就很像大家经常遇见的补偿操作,第一次发生异常失败,第二次重试后又成功了。

    image

    总结

    Spring-retry框架还是挺实用的,但不是万能的。

    优点是,简化了重试逻辑,提供了现成的重试策略,具备一定灵活性。

    缺点,也很明显,生产环境使用有风险,比如在复杂场景下配置的策略有问题,有可能会导致无限重试,这个后果不用说大家也能想象。

    所以,使用这个框架,一定要明确好场景再使用,我这里不推荐复杂场景下使用,因为君子不立于危墙之下

    好了,今天的知识点你学会了吗?

    完整代码:戳这里 --> Gitee


    喜欢请点赞+关注↑↑↑,持续分享干货哦~


    __EOF__

  • 本文作者: 程序员济癫
  • 本文链接: https://www.cnblogs.com/fulongyuanjushi/p/17920410.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    C陷阱与缺陷 第3章 语义“陷阱” 3.3 作为参数的数组声明
    人人都夸的IPD(集成产品开发)究竟好在哪?
    yolov5+shufflenet轻量化目标检测
    初识JVM(简单易懂),解开JVM神秘的面纱
    【二】2D测量 Metrology——add_metrology_object_circle_measure()算子
    PyTorch基础知识学习
    [st表][贪心]Loop 2022杭电多校第6场 1012
    集合_Collection_ArrayList扩容机制
    【IJCV】基于概率表征的半监督对比学习框架,FaceChain团队出品
    Mac怎么删除文件和软件?苹果电脑删除第三方软件方法
  • 原文地址:https://www.cnblogs.com/fulongyuanjushi/p/17920410.html