在web應(yīng)用中,由于網(wǎng)絡(luò)原因或其他不可預(yù)測的原因,應(yīng)用間會出現(xiàn)調(diào)用失敗的情形,通過配置重試策略可以有效解決外在原因?qū)е碌南到y(tǒ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í)重試
// 對應(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í)長:{}", 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,則會執(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)異常,則會執(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ù)代碼不會執(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í)行流程
通過RetryerBuilder構(gòu)建Retryer,調(diào)用Retryer#call,封裝業(yè)務(wù)代碼為其回調(diào)函數(shù)
- 開始循環(huán)執(zhí)行
- 由AttemptTimeLimiter#call執(zhí)行回調(diào)函數(shù)
- 將結(jié)果封裝為Attempt,包括兩種類型ResultAttempt,ExceptionAttempt。如果成功,記錄執(zhí)行結(jié)果、持續(xù)時(shí)長;如果失敗,記錄異常、持續(xù)時(shí)長
- 執(zhí)行監(jiān)聽RetyrListener#onRetry,可以配置多個(gè)監(jiān)聽
- 執(zhí)行拒絕斷言Predicate,根據(jù)返回值、執(zhí)行異常、返回異常類型判斷是否終止重試
- 如果滿足條件,則繼續(xù)重試;否則結(jié)束重試,并返回Attempt包含回調(diào)結(jié)果
- 根據(jù)終止策略StopStrategy判斷是否終止重試
- 根據(jù)等待策略WaitStrategy獲取等待時(shí)長
- 根據(jù)阻塞策略BlockStrategy與上一步等待時(shí)長阻塞重試,如果出現(xiàn)異常則拋出RetryException
- 重復(fù)執(zhí)行以上邏輯
配置
構(gòu)建Retryer主要通過RetryerBuilder.newBuilder()實(shí)現(xiàn),其相關(guān)配置如下:
配置 | 策略 | 名稱 | 描述 |
---|---|---|---|
AttemptTimeLimiters | 任務(wù)執(zhí)行時(shí)長限制 | ||
NoAttemptTimeLimit | 無時(shí)長限制 | ||
FixedAttemptTimeLimit | 固定時(shí)長限制 | ||
WaitStrategies | 重試等待策略 | ||
ExponentialWaitStrategy | 指數(shù)等待策略 | 按指數(shù)增加重試間隔時(shí)長,比如第一次2^1100、2^2100、2^3*100...最多300000 | |
FibonacciWaitStrategy | 斐波那契等待策略 | 1100、1100、2100、3100、5*100... | |
FixedWaitStrategy | 固定時(shí)長等待策略 | 按配置的固定間隔時(shí)間 | |
RandomWaitStrategy | 隨機(jī)時(shí)長等待策略 | 隨機(jī)間隔時(shí)間,可以設(shè)置隨機(jī)值范圍 | |
IncrementingWaitStrategy | 遞增等待策略 | 根據(jù)配置的初始值與增量進(jìn)行累加時(shí)間 | |
ExceptionWaitStrategy | 異常等待策略 | 根據(jù)異常類型指定等待時(shí)間 | |
CompositeWaitStrategy | 復(fù)合等待策略 | 可配置多個(gè)策略進(jìn)行組合 | |
BlockStrategies | 阻塞策略 | 根據(jù)WaitStrategies獲取阻塞時(shí)長 | |
ThreadSleepStrategy | 線程等等策略 | 通過Thread.sleet()實(shí)現(xiàn) | |
StopStrategies | 重試停止策略 | ||
NeverStopStrategy | 無限制策略 | ||
StopAfterAttemptStrategy | 限定次數(shù)策略 | ||
StopAfterDelayStrategy | 限定時(shí)長策略 | ||
NoAttemptTimeLimit | 限定次數(shù) |
注意
- AttemptTimeLimiter中的FixedAttemptTimeLimit依賴于guava中的SimpleTimeLimiter,但是在guava高版本中該類已經(jīng)成了私有類
總結(jié)
Guava Retrying模塊能夠通過簡單的將代碼實(shí)現(xiàn)業(yè)務(wù)邏輯重試的功能,并且其配置中包含了重試的次數(shù)、時(shí)長控制、重試阻塞、終止策略等等, 在項(xiàng)目中是非常常用的一項(xiàng)技術(shù)。
-
模塊
+關(guān)注
關(guān)注
7文章
2655瀏覽量
47293 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4284瀏覽量
62325 -
代碼
+關(guān)注
關(guān)注
30文章
4723瀏覽量
68236
發(fā)布評論請先 登錄
相關(guān)推薦
評論