写点什么

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

  • 2023-12-25
    福建
  • 本文字数:2903 字

    阅读完需:约 10 分钟

前言


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


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


框架介绍


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


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



正文


1、引入依赖


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


<!-- Spring-Retry --><dependency>    <groupId>org.springframework.retry</groupId>    <artifactId>spring-retry</artifactId></dependency><!-- Spring-AOP --><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-aop</artifactId></dependency>
复制代码


2、启动类注解


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


import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.retry.annotation.EnableRetry;
@SpringBootApplication@EnableRetrypublic 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;
/** * <p> * 短信服务工具类 * </p> * * @author 程序员济癫 * @since 2023-12-21 09:40 */@Slf4jpublic 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;
/** * <p> * 重试组件 * </p> * * @author 程序员济癫 * @since 2023-12-21 09:43 */@Slf4j@Componentpublic 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@SpringBootTestclass 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。



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

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



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

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

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



总结


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


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


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


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


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


文章转载自:程序员济癫

原文链接:https://www.cnblogs.com/fulongyuanjushi/p/17920410.html

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
5分钟攻略Spring-Retry框架实现经典重试场景_前端_快乐非自愿限量之名_InfoQ写作社区