SpringBoot高并發!業務方法重試就該使用它
環境:SpringBoot3.2.5
1. 簡介
業務功能重試機制通常在項目中是非常有必要的,特別是在處理外部系統調用(如HTTP請求、數據庫操作、消息隊列等)時。這些操作可能因網絡波動、服務暫時不可用等原因而失敗。重試機制可以提高系統的健壯性和用戶體驗,通過自動重試可以減少因單次失敗導致的整體業務中斷。本篇文章將介紹一款非常優秀的重試框架Fast-Retry。
Fast-Retry是一個高性能任務重試框架,支持百萬級別任務的并發重試處理。與主流的Spring-Retry, Guava-Retry等同步重試框架不同,Fast-Retry是一個支持異步重試框架,支持異步任務的重試、超時等待、回調。Spring-Retry, Guava-Retry均無法支持大批量任務的重試,因為會占用過多線程資源導致大量任務在等待處理,隨著任務數的增加,系統吞吐量大大降低,性能指數級降低,Fast-Retry的性能是前者的指數倍。
Fast-Retry,Spring-Retry,Guava-Retry性能對比
測試條件
- 測試線程池: 8個固定線程
- 單個任務邏輯: 輪詢5次,隔2秒重試一次,總耗時10秒
- 未測預計公式:當我們使用線程池的時候, 一般線程池中 總任務處理耗時 = 任務數/并發度 x 單個任務重試耗時
圖片
2. 實戰案例
2.1 引入依賴
<dependency>
<groupId>io.github.burukeyou</groupId>
<artifactId>fast-retry-all</artifactId>
<version>0.2.0</version>
</dependency>
配置開啟重試功能
@SpringBootApplication
@EnableFastRetry
public class SpringbootRetryApplication {}
接下來就可以通過@FastRetry注解配置類或方法。
2.2 基于編程重試
public String process() throws Exception {
// 自定義結果重試策略,如果返回結果不是"success"則進行重試
RetryResultPolicy<String> resultPolicy = result -> !result.equals("success");
FastRetryer<String> retryer = FastRetryBuilder.<String>builder()
// 重試次數
.attemptMaxTimes(2)
// 重試間隔
.waitRetryTime(1, TimeUnit.SECONDS)
// 發生異常后是否重試
.retryIfException(true)
// 什么類型的異常進行重試
.retryIfExceptionOfType(RuntimeException.class)
.exceptionRecover(true)
// 自定義結果重試策略
.resultPolicy(resultPolicy)
.build();
CompletableFuture<String> future = retryer.submit(() -> {
int r = new Random().nextInt(10) ;
System.out.printf("執行業務方法, 隨機值: %d%n", r) ;
if (r != 1) {
// 拋出異常,也會重試
// throw new RuntimeException("錯誤的參數: " + r) ;
return "dead" ;
}
return "success" ;
});
return future.get();
}
運行結果
成功
執行業務方法, 隨機值: 5
執行業務方法, 隨機值: 4
執行業務方法, 隨機值: 1
結果: success
失敗
圖片
超過重試次數后拋出異常,并且方法執行的最終結果返回:null。
2.3 基于注解
基于注解方式使用起來與spring-retry差不多。一個注解搞定。
@FastRetry(
retryWait = @RetryWait(delay = 2),
exceptionRecover = false,
maxAttempts = 2,
retryStrategy = PackRetryPolicy.class
)
public String business(Long id, String name) {
int r = new Random().nextInt(10) ;
System.out.printf("執行業務方法, 隨機值: %d%n", r) ;
if (r != 1) {
throw new RuntimeException("錯誤的參數: " + r) ;
}
return "success" ;
}
自定義方法返回結果重試策略。
public class PackRetryPolicy implements RetryResultPolicy<String> {
public boolean canRetry(String t) {
return !t.equals("success") ;
}
}
結果重試策略可以有多個。
2.4 異步任務重試
@FastRetry(
retryWait = @RetryWait(delay = 2),
maxAttempts = 2,
retryStrategy = PackRetryPolicy.class
)
public CompletableFuture<String> asyncBusiness(Long id, String name) {
return CompletableFuture.supplyAsync(() -> {
System.out.println("async 執行業務方法...") ;
int r = new Random().nextInt(10) ;
if (r != 1) {
// throw new RuntimeException("錯誤的參數: " + r) ;
return "1" ;
}
return "success" ;
}) ;
}
輸出結果
async 執行業務方法...
async 執行業務方法...
async 執行業務方法...
發生錯誤: com.burukeyou.retry.core.exceptions.FastRetryTimeOutException:
The maximum retry count has been exceeded after 2 times. Stop retry
同樣的代碼,如果換成spring-retry,如下:
// spring-retry的注解
@Retryable(maxAttempts = 2)
public CompletableFuture<String> asyncBusiness(Long id, String name) {
// 方法體與上面基本,一樣只不過其中拋出的是異常
}
輸出結果
async 執行業務方法...
發生錯誤: java.lang.RuntimeException: 錯誤的參數: 3
沒有進行重試,說明spring-retry不支持異步任務。
在spring-retry中你可以在注解中配置recover,指定一個恢復的方法(或降級的方法),在fast-retry中沒有這樣的功能。如下spring-retry示例:
@Retryable(maxAttempts = 2, recover = "businessRecover")
public String business(Long id, String name) {}
@Recover
private String businessRecover(Throwable th, Long id, String name) {}
當重試次數用盡后,將調用我們這里配置的businessRecover方法,同時在該方法中還可以獲取具體的異常信息。