百萬級任務重試框架 Fast-Retry

前言

假設你的系統裏有 100 萬個用戶,然後你要輪詢重試的獲取每個用戶的身份信息, 如果你還在使用 SpringRetry 和 GuavaRetry 之類的這種單任務的同步重試框架,那你可能到猴年馬月也處理不完, 即使加再多的機器和線程也是杯水車薪, 而 Fast-Retry 正是爲這種場景而生

Fast-Retry

一個高性能的多任務重試框架,支持百萬級任務的異步重試、以及支持編程式和註解聲明式等多種使用方式、 也支持自定義結果重試邏輯。

What is this?

與主流的 Spring-Retry, Guava-Retry 等單任務同步重試框架不同,Fast-Retry 是一個支持異步重試框架,支持異步任務的重試、超時等待、回調。Spring-Retry, Guava-Retry 均無法支持大批量任務的重試,即使加入線程池也無法解決,因爲實際每個重試任務都是單獨的同步邏輯,然後會會佔用過多線程資源導致大量任務在等待處理,隨着任務數的增加,系統吞吐量大大降低,性能指數級降低,而 Fast-Retry 在異步重試下的性能是前者的指數倍。下圖是三者的性能對比

可以看到即使是處理 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 註解的方法進行代理, 提供了重試任務註解聲明式的使用方式:

下面定義等價於 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 方法去等待異步重試任務的執行結果。

其他

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