今天分享乾貨,控制了篇幅,5分鐘
內就能看完學會。
主題是Spring-Retry
框架的應用,做了一個很清晰的案例,程式碼可下載自測。
Spring-Retry
框架是Spring自帶的功能,具備間隔重試
、包含異常
、排除異常
、控制重試頻率
等特點,是專案開發中很實用的一種框架。
本篇所用框架的版本如下:
技術 | 版本 |
---|---|
Java | 17 |
SpringBoot | 3.2 |
Spring-retry | 2.0.4 |
坑點:需要引入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>
坑點:很容易一時疏忽忘記啟動類開啟@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);
}
}
我們模擬一個傳簡訊功能,根據亂數分別作為成功、失敗、丟擲各種異常的入口。
這裡丟擲幾種異常的目的,是為了後面演示出重試註解引數產生的效果。
import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j;
/**
* <p>
* 簡訊服務工具類
* </p>
*
* @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;
};
}
}
我們單獨寫一個用於重試呼叫的元件類,用於業務類呼叫。
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
@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
註解引數說明:
會觸發
重試機制,多個異常則以陣列形式定義。不會觸發
重試機制,多個異常則以陣列形式定義。delay
-延遲時間(s),multiplier
-重試時間的倍數(比如設定為2,重試4次的話,補償機制就是分別間隔2s、4s、8s做重試)@Recover
註解說明:用於兜底,當 超出了最大重試次數
或 丟擲了不進行重試的異常
時,直接執行該註解宣告的兜底方法。
PS:順便提一句,如果是
SpringBoot2.x
的版本,這裡@Retryable
註解的retryFor
引數對應的是include
,noRetryFor
引數對應的是exclude
,可以直接點進去看原始碼便一目瞭然。
我們編寫一個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);
}
}
第1次測試時,可以看到,亂數剛好都是1,走的是陣列越界異常。
而這個異常在retryFor中定義了,所以執行了4次,直到結束,最後進入了兜底方法。
同時,可以看到,執行4次的頻率也和預想一樣是2s、4s、8s。
第2次測試時,可以看到,亂數是3,走的是空指標異常。
而這個異常在noRetryFor中定義了,所以接下來直接進入了兜底方法。
第3次測試時,可以看到,第一次亂數是0,走的引數異常,在retryFor中,所以2s後繼續重試。
然後亂數是2,表示業務成功,所以直接返回了true。
這個場景就很像大家經常遇見的補償操作,第一次發生異常失敗,第二次重試後又成功了。
Spring-retry
框架還是挺實用的,但不是萬能的。
優點是,簡化了重試邏輯,提供了現成的重試策略,具備一定靈活性。
缺點,也很明顯,生產環境使用有風險,比如在複雜場景下設定的策略有問題,有可能會導致無限重試,這個後果不用說大家也能想象。
所以,使用這個框架,一定要明確好場景再使用,我這裡不推薦複雜場景下使用,因為君子不立於危牆之下
。
好了,今天的知識點你學會了嗎?
完整程式碼:戳這裡 --> Gitee
喜歡請點贊+關注↑↑↑,持續分享乾貨哦~