【Guava】基于guava的重試組件Guava-Retryer

一、使用場(chǎng)景

在日常開發(fā)中药薯,我們經(jīng)常會(huì)遇到需要調(diào)用外部服務(wù)和接口的場(chǎng)景饿序。外部服務(wù)對(duì)于調(diào)用者來說一般都是不可靠的熬苍,尤其是在網(wǎng)絡(luò)環(huán)境比較差的情況下劳澄,網(wǎng)絡(luò)抖動(dòng)很容易導(dǎo)致請(qǐng)求超時(shí)等異常情況地技,這時(shí)候就需要使用失敗重試策略重新調(diào)用 API 接口來獲取。重試策略在服務(wù)治理方面也有很廣泛的使用秒拔,通過定時(shí)檢測(cè)乓土,來查看服務(wù)是否存活(
Active)。

Guava Retrying 是一個(gè)靈活方便的重試組件溯警,包含了多種的重試策略,而且擴(kuò)展起來非常容易狡相。

用作者的話來說:

This is a small extension to Google’s Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.

使用 Guava-retrying 你可以自定義來執(zhí)行重試梯轻,同時(shí)也可以監(jiān)控每次重試的結(jié)果和行為,最重要的基于 Guava 風(fēng)格的重試方式真的很方便尽棕。

二喳挑、代碼示例

以下會(huì)簡(jiǎn)單列出 guava-retrying 的使用方式:

  • 如果拋出 IOException 則重試,如果返回結(jié)果為 null 或者等于 2 則重試,固定等待時(shí)長(zhǎng)為 300 ms,最多嘗試 3 次伊诵;
Callable<Integer> task = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return 2;
    }
};

Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
        .retryIfResult(Predicates.<Integer>isNull())
        .retryIfResult(Predicates.equalTo(2))
        .retryIfExceptionOfType(IOException.class)
        .withStopStrategy(StopStrategies.stopAfterAttempt(3))
        .withWaitStrategy(WaitStrategies.fixedWait(300, TimeUnit.MILLISECONDS))
        .build();
try {
    retryer.call(task);
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (RetryException e) {
    e.printStackTrace();
}
  • 出現(xiàn)異常則執(zhí)行重試单绑,每次任務(wù)執(zhí)行最長(zhǎng)執(zhí)行時(shí)間限定為 3 s,重試間隔時(shí)間初始為 3 s曹宴,最多重試 1 分鐘搂橙,隨著重試次數(shù)的增加每次遞增 1 s,每次重試失敗笛坦,打印日志区转;
@Override
    public Integer call() throws Exception {
        return 2;
    }
};

Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
        .retryIfException()
        .withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS))
        .withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS,1,TimeUnit.SECONDS))
        .withAttemptTimeLimiter(AttemptTimeLimiters.<Integer>fixedTimeLimit(3,TimeUnit.SECONDS))
        .withRetryListener(new RetryListener() {
            @Override
            public <V> void onRetry(Attempt<V> attempt) {
                if (attempt.hasException()){
                    attempt.getExceptionCause().printStackTrace();
                }
            }
        })
        .build();
try {
    retryer.call(task);
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (RetryException e) {
    e.printStackTrace();
}

三、核心執(zhí)行邏輯

long startTime = System.nanoTime();
for (int attemptNumber = 1; ; attemptNumber++) {
    Attempt<V> attempt;
    try {
        // 執(zhí)行成功
        V result = attemptTimeLimiter.call(callable);
        attempt = new ResultAttempt<V>(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
    } catch (Throwable t) {
        // 執(zhí)行失敗
        attempt = new ExceptionAttempt<V>(t, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
    }
    // 監(jiān)聽器處理
    for (RetryListener listener : listeners) {
        listener.onRetry(attempt);
    }
    // 是否符合終止策略
    if (!rejectionPredicate.apply(attempt)) {
        return attempt.get();
    }
    // 是否符合停止策略
    if (stopStrategy.shouldStop(attempt)) {
        throw new RetryException(attemptNumber, attempt);
    } else {
        // 計(jì)算下次重試間隔時(shí)間
        long sleepTime = waitStrategy.computeSleepTime(attempt);
        try {
            blockStrategy.block(sleepTime);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RetryException(attemptNumber, attempt);
        }
    }
}

四版扩、依賴引入

<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>2.0.0</version>
</dependency>

默認(rèn)的guava中也有包含废离。

五、主要接口介紹:

  • Attempt:一次執(zhí)行任務(wù)礁芦;

  • AttemptTimeLimiter:?jiǎn)未稳蝿?wù)執(zhí)行時(shí)間限制(如果單次任務(wù)執(zhí)行超時(shí)蜻韭,則終止執(zhí)行當(dāng)前任務(wù));

  • BlockStrategies:任務(wù)阻塞策略(通俗的講就是當(dāng)前任務(wù)執(zhí)行完柿扣,下次任務(wù)還沒開始這段時(shí)間做什么……)肖方,默認(rèn)策略為:BlockStrategies.THREAD_SLEEP_STRATEGY 也就是調(diào)用 Thread.sleep(sleepTime);

  • RetryException:重試異常;

  • RetryListener:自定義重試監(jiān)聽器窄刘,可以用于異步記錄錯(cuò)誤日志窥妇;

  • StopStrategy:停止重試策略,提供三種:

    • StopAfterDelayStrategy :設(shè)定一個(gè)最長(zhǎng)允許的執(zhí)行時(shí)間娩践;比如設(shè)定最長(zhǎng)執(zhí)行10s活翩,無論任務(wù)執(zhí)行次數(shù),只要重試的時(shí)候超出了最長(zhǎng)時(shí)間翻伺,則任務(wù)終止材泄,并返回重試異常RetryException;
    • NeverStopStrategy :不停止吨岭,用于需要一直輪訓(xùn)知道返回期望結(jié)果的情況拉宗;
    • StopAfterAttemptStrategy :設(shè)定最大重試次數(shù),如果超出最大重試次數(shù)則停止重試辣辫,并返回重試異常旦事;
  • WaitStrategy:等待時(shí)長(zhǎng)策略(控制時(shí)間間隔),返回結(jié)果為下次執(zhí)行時(shí)長(zhǎng):

    • FixedWaitStrategy:固定等待時(shí)長(zhǎng)策略急灭;
    • RandomWaitStrategy:隨機(jī)等待時(shí)長(zhǎng)策略(可以提供一個(gè)最小和最大時(shí)長(zhǎng)姐浮,等待時(shí)長(zhǎng)為其區(qū)間隨機(jī)值)
    • IncrementingWaitStrategy:遞增等待時(shí)長(zhǎng)策略(提供一個(gè)初始值和步長(zhǎng),等待時(shí)間隨重試次數(shù)增加而增加)
    • ExponentialWaitStrategy:指數(shù)等待時(shí)長(zhǎng)策略葬馋;
    • FibonacciWaitStrategy :Fibonacci 等待時(shí)長(zhǎng)策略卖鲤;
    • ExceptionWaitStrategy :異常時(shí)長(zhǎng)等待策略肾扰;
    • CompositeWaitStrategy :復(fù)合時(shí)長(zhǎng)等待策略;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蛋逾,一起剝皮案震驚了整個(gè)濱河市集晚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌区匣,老刑警劉巖偷拔,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沉颂,居然都是意外死亡条摸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門铸屉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钉蒲,“玉大人,你說我怎么就攤上這事彻坛∏晏洌” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵昌屉,是天一觀的道長(zhǎng)钙蒙。 經(jīng)常有香客問我,道長(zhǎng)间驮,這世上最難降的妖魔是什么躬厌? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮竞帽,結(jié)果婚禮上扛施,老公的妹妹穿的比我還像新娘。我一直安慰自己屹篓,他們只是感情好疙渣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著堆巧,像睡著了一般妄荔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谍肤,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天啦租,我揣著相機(jī)與錄音,去河邊找鬼荒揣。 笑死刷钢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乳附。 我是一名探鬼主播内地,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赋除!你這毒婦竟也來了阱缓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤举农,失蹤者是張志新(化名)和其女友劉穎荆针,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颁糟,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡航背,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棱貌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玖媚。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖婚脱,靈堂內(nèi)的尸體忽然破棺而出今魔,到底是詐尸還是另有隱情,我是刑警寧澤障贸,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布错森,位于F島的核電站,受9級(jí)特大地震影響篮洁,放射性物質(zhì)發(fā)生泄漏涩维。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一袁波、第九天 我趴在偏房一處隱蔽的房頂上張望瓦阐。 院中可真熱鬧,春花似錦锋叨、人聲如沸垄分。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽薄湿。三九已至,卻和暖如春偷卧,著一層夾襖步出監(jiān)牢的瞬間豺瘤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工听诸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坐求,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓晌梨,卻偏偏與公主長(zhǎng)得像桥嗤,于是被迫代替她去往敵國(guó)和親须妻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容