百萬級任務重試框架 Fast-Retry
前言
假設你的系統裏有 100 萬個用戶,然後你要輪詢重試的獲取每個用戶的身份信息, 如果你還在使用 SpringRetry 和 GuavaRetry 之類的這種單任務的同步重試框架,那你可能到猴年馬月也處理不完, 即使加再多的機器和線程也是杯水車薪, 而 Fast-Retry 正是爲這種場景而生
Fast-Retry
一個高性能的多任務重試框架,支持百萬級任務的異步重試、以及支持編程式和註解聲明式等多種使用方式、 也支持自定義結果重試邏輯。
What is this?
與主流的 Spring-Retry, Guava-Retry 等單任務同步重試框架不同,Fast-Retry 是一個支持異步重試框架,支持異步任務的重試、超時等待、回調。Spring-Retry, Guava-Retry 均無法支持大批量任務的重試,即使加入線程池也無法解決,因爲實際每個重試任務都是單獨的同步邏輯,然後會會佔用過多線程資源導致大量任務在等待處理,隨着任務數的增加,系統吞吐量大大降低,性能指數級降低,而 Fast-Retry 在異步重試下的性能是前者的指數倍。下圖是三者的性能對比
-
測試線程池: 8 個固定線程
-
單個任務邏輯: 輪詢 5 次,隔 2 秒重試一次,總耗時 10 秒
-
未測預計公式: 當我們使用線程池的時候, 一般線程池中 總任務處理耗時 = 任務數 / 併發度 x 單個任務重試耗時
可以看到即使是處理 100 萬個任務,Fast-Retry 的性能也比 Spring-Retry 和 Guava-Retry 處理在 50 個任務時的性能還要快的多的多屬實降維打擊,這麼快的祕密在於除了是異步,重要的是當別人在重試間隔裏休息的時候,Fast-Retry 還在不停忙命的工作着。即使拋開性能不談, SpringRetry 使用繁瑣,不支持根據結果的進行重試,GuavaRetry 雖然支持,但是又沒有提供註解聲明式的使用。
快速開始
引入依賴
xml複製代碼 <dependency>
<groupId>io.github.burukeyou</groupId>
<artifactId>fast-retry-all</artifactId>
<version>0.2.0</version>
</dependency>
有以下三種方式去構建我們的重試任務
使用重試隊列
RetryTask 就是可以配置我們重試任務的一些邏輯,比如怎麼重試,怎麼獲取重試結果,隔多久後重試,在什麼情況下重試。它可以幫助我們更加自由的去構建重試任務的邏輯。但如果只是簡單使用,強烈建議使用 FastRetryBuilder 或者 @FastRetry 註解 RetryQueue 就是一個執行和調度我們重試任務的核心角色,其在使用上與線程池的 API 方法基本一致
ExecutorService executorService = Executors.newFixedThreadPool(8);
RetryQueue queue = new FastRetryQueue(executorService);
RetryTask<String> task = new RetryTask<String>() {
int result = 0 ;
// 下一次重試的間隔
@Override
public long waitRetryTime() {
return 2000;
}
// 執行重試,每次重試回調此方法
@Override
public boolean retry() {
return ++result < 5;
}
// 獲取重試結果
@Override
public String getResult() {
return result + "";
}
};
CompletableFuture<String> future = queue.submit(task);
log.info("任務結束 結果:{}",future.get());
使用 FastRetryBuilder
底層還是使用的 RetryQueue 去處理, 只是幫我們簡化了構建 RetryTask 的邏輯
RetryResultPolicy<String> resultPolicy = result -> result.equals("444");
FastRetryer<String> retryer = FastRetryBuilder.<String>builder()
.attemptMaxTimes(3)
.waitRetryTime(3, TimeUnit.SECONDS)
.retryIfException(true)
.retryIfExceptionOfType(TimeoutException.class)
.exceptionRecover(true)
.resultPolicy(resultPolicy)
.build();
CompletableFuture<String> future = retryer.submit(() -> {
log.info("重試");
//throw new Exception("test");
//int i = 1/0;
if (0 < 10){
throw new TimeoutException("test");
}
return "444";
});
String o = future.get();
log.info("結果{}", o);
使用 @FastRetry 註解
底層還是使用的 RetryQueue 去處理, 只是幫我們簡化了構建 RetryTask 的邏輯,並且與 Spring 進行整合能對 Spring 的 bean 標記了 FastRetry 註解的方法進行代理, 提供了重試任務註解聲明式的使用方式:
-
依賴 Spring 環境,所以需要在 Spring 配置類加上 @EnableFastRetry 註解啓用配置 , 這個 @FastRetry 註解的使用纔會生效
-
如果將結果類型使用 CompletableFuture 包裝,自動進行異步輪詢返回,否則同步阻塞等待重試結果。(推薦)
下面定義等價於 RetryQueue.execute 方法
// 如果發生異常,每隔兩秒重試一次
@FastRetry(retryWait = @RetryWait(delay = 2))
public String retryTask(){
return "success";
}
下面定義等價於 RetryQueue.submit 方法, 支持異步輪詢
@FastRetry(retryWait = @RetryWait(delay = 2))
public CompletableFuture<String> retryTask(){
return CompletableFuture.completedFuture("success");
}
自定義重試註解
如果不喜歡或者需要更加通用化的貼近業務的重試註解,提供一些默認的參數和處理邏輯,可以自行定義一個重試註解並標記上 @FastRetry 並指定 factory,然後實現 AnnotationRetryTaskFactory 接口實現自己的構建重試任務的邏輯即可。@FastRetry 默認實現就是:FastRetryAnnotationRetryTaskFactory
使用建議
無論是使用以上哪種方式去構建你的重試任務,都建議使用異步重試的方法,即返回結果是 CompletableFuture 的方法, 然後使用 CompletableFuture 的 whenComplete 方法去等待異步重試任務的執行結果。
其他
-
github 項目地址 :
https://github.com/burukeYou/fast-retry
具體使用案例見 fast-retry-demo 模塊 -
maven 倉庫地址:
https://central.sonatype.com/artifact/io.github.burukeyou/fast-retry-all
還在迭代中,第一個版本主要專注於多重試任務的處理, 還需要什麼好玩的功能和特徵請留下你的意見感謝。覺得有用或者能學到點什麼可以 star 下哈哈
作者:李白的手機
來源:https://juejin.cn/post/7337989768637939739
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/-X5ctyKomYxbvUci_qDZZA