javascript
5分钟攻略Spring-Retry框架实现经典重试场景
前言
今天分享干貨,控制了篇幅,5分鐘內(nèi)就能看完學(xué)會。
主題是Spring-Retry框架的應(yīng)用,做了一個很清晰的案例,代碼可下載自測。
框架介紹
Spring-Retry框架是Spring自帶的功能,具備間隔重試、包含異常、排除異常、控制重試頻率等特點(diǎn),是項(xiàng)目開發(fā)中很實(shí)用的一種框架。
本篇所用框架的版本如下:
| 技術(shù) | 版本 |
|---|---|
| Java | 17 |
| SpringBoot | 3.2 |
| Spring-retry | 2.0.4 |
正文
1、引入依賴
坑點(diǎn):需要引入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、啟動類注解
坑點(diǎn):很容易一時疏忽忘記啟動類開啟@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、模擬發(fā)短信
我們模擬一個發(fā)短信功能,根據(jù)隨機(jī)數(shù)分別作為成功、失敗、拋出各種異常的入口。
這里拋出幾種異常的目的,是為了后面演示出重試注解參數(shù)產(chǎn)生的效果。
import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
/**
* <p>
* 短信服務(wù)工具類
* </p>
*
* @author 程序員濟(jì)癲
* @since 2023-12-21 09:40
*/
@Slf4j
public class SmsUtil {
/**
* 發(fā)送短信
*/
public static boolean sendSms() {
// 使用隨機(jī)數(shù)模擬重試場景
int num = RandomUtil.randomInt(4);
log.info("[SmsUtil][sendSms]>>>> random num = {}", num);
return switch (num) {
case 0 ->
// 模擬發(fā)生參數(shù)異常
throw new IllegalArgumentException("參數(shù)有誤!");
case 1 ->
// 模擬發(fā)生數(shù)組越界異常
throw new ArrayIndexOutOfBoundsException("數(shù)組越界!");
case 2 ->
// 模擬成功
true;
case 3 ->
// 模擬發(fā)生空指針界異常
throw new NullPointerException();
default ->
// 未成功則返回false
false;
};
}
}
4、Retry應(yīng)用
我們單獨(dú)寫一個用于重試調(diào)用的組件類,用于業(yè)務(wù)類調(diào)用。
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 程序員濟(jì)癲
* @since 2023-12-21 09:43
*/
@Slf4j
@Component
public class RetryComponent {
/**
* 重試機(jī)制發(fā)送短信
*/
@Retryable(
retryFor = {IllegalArgumentException.class, ArrayIndexOutOfBoundsException.class},
noRetryFor = {NullPointerException.class},
maxAttempts = 4,
backoff = @Backoff(delay = 2000L, multiplier = 2)
)
public boolean sendSmsRetry() {
log.info("[RetryComponent][sendSmsRetry]>>>> 當(dāng)前時間:{}", getNowTime());
return SmsUtil.sendSms();
}
/**
* 兜底方法,規(guī)則:
* 1、超出了最大重試次數(shù);
* 2、拋出了不進(jìn)行重試的異常;
*/
@Recover
public boolean recover() {
log.info("[RetryComponent][recover]>>>> 短信發(fā)送次數(shù)過多,請稍后重試!");
return false;
}
/**
* 獲取當(dāng)前時間
*/
private String getNowTime() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
@Retryable注解參數(shù)說明:
- retryFor:此參數(shù)包含的異常
會觸發(fā)重試機(jī)制,多個異常則以數(shù)組形式定義。 - noRetryFor:此參數(shù)包含的異常
不會觸發(fā)重試機(jī)制,多個異常則以數(shù)組形式定義。 - maxAttempts:重試最大次數(shù),不定義則默認(rèn)3次。
- backoff:定義補(bǔ)償機(jī)制,
delay-延遲時間(s),multiplier-重試時間的倍數(shù)(比如設(shè)置為2,重試4次的話,補(bǔ)償機(jī)制就是分別間隔2s、4s、8s做重試)
@Recover注解說明:用于兜底,當(dāng) 超出了最大重試次數(shù) 或 拋出了不進(jìn)行重試的異常 時,直接執(zhí)行該注解聲明的兜底方法。
PS:順便提一句,如果是
SpringBoot2.x的版本,這里@Retryable注解的retryFor參數(shù)對應(yīng)的是include,noRetryFor參數(shù)對應(yīng)的是exclude,可以直接點(diǎn)進(jìn)去看源碼便一目了然。
5、JunitTest測試
我們編寫一個Junit測試類來測試重試的效果,并打印出結(jié)果信息。
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次測試時,可以看到,隨機(jī)數(shù)剛好都是1,走的是數(shù)組越界異常。
而這個異常在retryFor中定義了,所以執(zhí)行了4次,直到結(jié)束,最后進(jìn)入了兜底方法。
同時,可以看到,執(zhí)行4次的頻率也和預(yù)想一樣是2s、4s、8s。
第2次測試時,可以看到,隨機(jī)數(shù)是3,走的是空指針異常。
而這個異常在noRetryFor中定義了,所以接下來直接進(jìn)入了兜底方法。
第3次測試時,可以看到,第一次隨機(jī)數(shù)是0,走的參數(shù)異常,在retryFor中,所以2s后繼續(xù)重試。
然后隨機(jī)數(shù)是2,表示業(yè)務(wù)成功,所以直接返回了true。
這個場景就很像大家經(jīng)常遇見的補(bǔ)償操作,第一次發(fā)生異常失敗,第二次重試后又成功了。
總結(jié)
Spring-retry框架還是挺實(shí)用的,但不是萬能的。
優(yōu)點(diǎn)是,簡化了重試邏輯,提供了現(xiàn)成的重試策略,具備一定靈活性。
缺點(diǎn),也很明顯,生產(chǎn)環(huán)境使用有風(fēng)險,比如在復(fù)雜場景下配置的策略有問題,有可能會導(dǎo)致無限重試,這個后果不用說大家也能想象。
所以,使用這個框架,一定要明確好場景再使用,我這里不推薦復(fù)雜場景下使用,因?yàn)?code>君子不立于危墻之下。
好了,今天的知識點(diǎn)你學(xué)會了嗎?
完整代碼:戳這里 --> Gitee
喜歡請點(diǎn)贊+關(guān)注↑↑↑,持續(xù)分享干貨哦~
總結(jié)
以上是生活随笔為你收集整理的5分钟攻略Spring-Retry框架实现经典重试场景的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 欧盟与苹果公司对簿公堂!要求补缴143亿
- 下一篇: 一文掌握 Kubernetes 证书