Java利用配置重試策略解決超時(shí)問(wèn)題
在web應(yīng)用中,由于網(wǎng)絡(luò)原因或其他不可預(yù)測(cè)的原因,應(yīng)用間會(huì)出現(xiàn)調(diào)用失敗的情形,通過(guò)配置重試策略可以有效解決外在原因?qū)е碌南到y(tǒng)故障。
使用場(chǎng)景
- 微服務(wù)間各個(gè)服務(wù)模塊間的調(diào)用
- 第三方模塊遠(yuǎn)程交易調(diào)用
- 非業(yè)務(wù)異常導(dǎo)致可能失敗的情況
示例
構(gòu)建Retryer
private?Retryer?retryer?=?RetryerBuilder.newBuilder() ????????.retryIfException()?//?異常時(shí)重試 ????????.retryIfResult(input?->?input!=null?&&?input?instanceof?Boolean?&&?!Boolean.valueOf((Boolean)?input))?//?返回值為false時(shí)重試 ????????//?對(duì)應(yīng)Future獲取超時(shí)時(shí)間 ????????.withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(4,?TimeUnit.SECONDS,Executors.newFixedThreadPool(2)))?//重試次數(shù)限制 ????????.withRetryListener(new?RetryListener()?{?//?重試執(zhí)行邏輯 ????????????@Override ????????????public?<V>?void?onRetry(Attempt<V>?attempt)?{ ????????????????log.info("onRetry ->?重試次數(shù):{},距第一次重試時(shí)長(zhǎng):{}",?attempt.getAttemptNumber(),attempt.getDelaySinceFirstAttempt()); ????????????????if(attempt.hasException()){?//?是否異常導(dǎo)致重試 ????????????????????Throwable?exception?=?attempt.getExceptionCause();?//?執(zhí)行的異常 ????????????????????log.info("異常:{}",?exception); ????????????????} ????????????????if(attempt.hasResult()){?//?是否有返回 ????????????????????V?result?=?attempt.getResult(); ????????????????????log.info("返回:{}",result); ????????????????} ????????????} ????????}) ????????//?控制每次重試間隔時(shí)間,如果AttemptTimeLimiter設(shè)置多線程 ????????.withWaitStrategy(WaitStrategies.fixedWait(3,TimeUnit.SECONDS))?//?等待策略 ????????.withBlockStrategy(BlockStrategies.threadSleepStrategy())?//?阻塞策略 ????????// ????????.withStopStrategy(StopStrategies.stopAfterAttempt(5))?//?停止策略 ????????.build();
使用Retryer讓業(yè)務(wù)代碼擁有重試能力
前兩次執(zhí)行時(shí)模擬返回false,則會(huì)執(zhí)行重試;當(dāng)?shù)?次時(shí),正常執(zhí)行業(yè)務(wù)代碼并返回true,結(jié)束重試
@Test public?void?retryWhenResult()?throws?ExecutionException,?RetryException?{ ???retryer.call(()?->?{ ???????if(counter.incrementAndGet()?==?3){//?模擬前2此返回false,觸發(fā)重試 ???????????log.info("?執(zhí)行業(yè)務(wù)代碼:{}次",counter.get()); ???????????return?true; ???????} ???????return?false;? ???}); }
模擬前3次出現(xiàn)異常,則會(huì)執(zhí)行重試;當(dāng)?shù)?次時(shí),正常執(zhí)行業(yè)務(wù)代碼,結(jié)束重試
@Test public?void?retryWhenException()?throws?ExecutionException,?RetryException?{ ????retryer.call(()?->?{ ????????if(?counter.getAndIncrement()?==?3?){//?模擬前5此出現(xiàn)異常,觸發(fā)重試 ????????????return?counter; ????????} ????????log.info("?執(zhí)行業(yè)務(wù)代碼:?{}次",?counter.get()); ?????????throw?new?RuntimeException("ERROR");? ????}); }
模擬前5此出現(xiàn)異常,由于Retryer配置重試次數(shù)為5,則最終業(yè)務(wù)代碼不會(huì)執(zhí)行
@Test public?void?retryWhenResultOnFailure()?throws?ExecutionException,?RetryException?{ ????retryer.call(()?->?{ ????????if(counter.incrementAndGet()?==?8){//?模擬前7此返回false,由于配置重試5次,因此最終失敗 ????????????log.info("?執(zhí)行業(yè)務(wù)代碼:{}次",counter.get()); ????????????return?true; ????????} ????????return?false; ????}); }
執(zhí)行流程
執(zhí)行流程
通過(guò)RetryerBuilder構(gòu)建Retryer,調(diào)用Retryer#call,封裝業(yè)務(wù)代碼為其回調(diào)函數(shù)
- 開(kāi)始循環(huán)執(zhí)行
- 由AttemptTimeLimiter#call執(zhí)行回調(diào)函數(shù)
- 將結(jié)果封裝為Attempt,包括兩種類型ResultAttempt,ExceptionAttempt。如果成功,記錄執(zhí)行結(jié)果、持續(xù)時(shí)長(zhǎng);如果失敗,記錄異常、持續(xù)時(shí)長(zhǎng)
- 執(zhí)行監(jiān)聽(tīng)RetyrListener#onRetry,可以配置多個(gè)監(jiān)聽(tīng)
- 執(zhí)行拒絕斷言Predicate,根據(jù)返回值、執(zhí)行異常、返回異常類型判斷是否終止重試
- 如果滿足條件,則繼續(xù)重試;否則結(jié)束重試,并返回Attempt包含回調(diào)結(jié)果
- 根據(jù)終止策略StopStrategy判斷是否終止重試
- 根據(jù)等待策略WaitStrategy獲取等待時(shí)長(zhǎng)
- 根據(jù)阻塞策略BlockStrategy與上一步等待時(shí)長(zhǎng)阻塞重試,如果出現(xiàn)異常則拋出RetryException
- 重復(fù)執(zhí)行以上邏輯
配置
構(gòu)建Retryer主要通過(guò)RetryerBuilder.newBuilder()實(shí)現(xiàn),其相關(guān)配置如下:
配置 | 策略 | 名稱 | 描述 |
---|---|---|---|
AttemptTimeLimiters | 任務(wù)執(zhí)行時(shí)長(zhǎng)限制 | ||
NoAttemptTimeLimit | 無(wú)時(shí)長(zhǎng)限制 | ||
FixedAttemptTimeLimit | 固定時(shí)長(zhǎng)限制 | ||
WaitStrategies | 重試等待策略 | ||
ExponentialWaitStrategy | 指數(shù)等待策略 | 按指數(shù)增加重試間隔時(shí)長(zhǎng),比如第一次2^1100、2^2100、2^3*100...最多300000 | |
FibonacciWaitStrategy | 斐波那契等待策略 | 1100、1100、2100、3100、5*100... | |
FixedWaitStrategy | 固定時(shí)長(zhǎng)等待策略 | 按配置的固定間隔時(shí)間 | |
RandomWaitStrategy | 隨機(jī)時(shí)長(zhǎng)等待策略 | 隨機(jī)間隔時(shí)間,可以設(shè)置隨機(jī)值范圍 | |
IncrementingWaitStrategy | 遞增等待策略 | 根據(jù)配置的初始值與增量進(jìn)行累加時(shí)間 | |
ExceptionWaitStrategy | 異常等待策略 | 根據(jù)異常類型指定等待時(shí)間 | |
CompositeWaitStrategy | 復(fù)合等待策略 | 可配置多個(gè)策略進(jìn)行組合 | |
BlockStrategies | 阻塞策略 | 根據(jù)WaitStrategies獲取阻塞時(shí)長(zhǎng) | |
ThreadSleepStrategy | 線程等等策略 | 通過(guò)Thread.sleet()實(shí)現(xiàn) | |
StopStrategies | 重試停止策略 | ||
NeverStopStrategy | 無(wú)限制策略 | ||
StopAfterAttemptStrategy | 限定次數(shù)策略 | ||
StopAfterDelayStrategy | 限定時(shí)長(zhǎng)策略 | ||
NoAttemptTimeLimit | 限定次數(shù) |
注意
AttemptTimeLimiter中的FixedAttemptTimeLimit依賴于guava中的SimpleTimeLimiter,但是在guava高版本中該類已經(jīng)成了私有類
總結(jié)
Guava Retrying模塊能夠通過(guò)簡(jiǎn)單的將代碼實(shí)現(xiàn)業(yè)務(wù)邏輯重試的功能,并且其配置中包含了重試的次數(shù)、時(shí)長(zhǎng)控制、重試阻塞、終止策略等等, 在項(xiàng)目中是非常常用的一項(xiàng)技術(shù)。
到此這篇關(guān)于Java利用配置重試策略解決超時(shí)問(wèn)題的文章就介紹到這了,更多相關(guān)Java配置重試策略解決超時(shí)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談在Spring中如何使用數(shù)據(jù)源(DBCP、C3P0、JNDI)
這篇文章主要介紹了淺談在Spring中如何使用數(shù)據(jù)源(DBCP、C3P0、JNDI),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10Dubbo+zookeeper?最簡(jiǎn)單的分布式搭建方案
這篇文章主要介紹了Dubbo+zookeeper?最簡(jiǎn)單的分布式搭建,本例采用?dubbo+zookeeper?搭建分布式系統(tǒng),環(huán)境?jdk1.8,需要的朋友可以參考下2022-04-04Java實(shí)現(xiàn)多線程大批量同步數(shù)據(jù)(分頁(yè))
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)多線程大批量同步數(shù)據(jù)(分頁(yè)),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08MyBatis-Plus之邏輯刪除的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis-Plus之邏輯刪除的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11如何在springMVC的controller中獲取request
這篇文章主要介紹了如何在springMVC的controller中獲取request,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Java實(shí)現(xiàn)學(xué)生選課管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)學(xué)生選課管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07Java別說(shuō)取余(%)運(yùn)算簡(jiǎn)單你真的會(huì)嗎
這篇文章主要介紹了Java別說(shuō)取余(%)運(yùn)算簡(jiǎn)單你真的會(huì)嗎,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07java接口返回參數(shù)按照請(qǐng)求參數(shù)進(jìn)行排序方式
這篇文章主要介紹了java接口返回參數(shù)按照請(qǐng)求參數(shù)進(jìn)行排序方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java使用橋接模式實(shí)現(xiàn)開(kāi)關(guān)和電燈照明功能詳解
這篇文章主要介紹了Java使用橋接模式實(shí)現(xiàn)開(kāi)關(guān)和電燈照明功能,較為詳細(xì)的講述了橋接模式的概念、原理并結(jié)合實(shí)例形式分析了Java使用橋接模式實(shí)現(xiàn)開(kāi)關(guān)和電燈照明功能相關(guān)操作步驟與注意事項(xiàng),需要的朋友可以參考下2018-05-05