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