在web應用中,由于網絡原因或其他不可預測的原因,應用間會出現調用失敗的情形,通過配置重試策略可以有效解決外在原因導致的系統故障。
使用場景
- 微服務間各個服務模塊間的調用
- 第三方模塊遠程交易調用
- 非業務異常導致可能失敗的情況
示例
構建Retryer
private Retryer retryer = RetryerBuilder.newBuilder()
.retryIfException() // 異常時重試
.retryIfResult(input - > input!=null && input instanceof Boolean && !Boolean.valueOf((Boolean) input)) // 返回值為false時重試
// 對應Future獲取超時時間
.withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(4, TimeUnit.SECONDS,Executors.newFixedThreadPool(2))) //重試次數限制
.withRetryListener(new RetryListener() { // 重試執行邏輯
@Override
public < V > void onRetry(Attempt< V > attempt) {
log.info("onRetry - > 重試次數:{},距第一次重試時長:{}", attempt.getAttemptNumber(),attempt.getDelaySinceFirstAttempt());
if(attempt.hasException()){ // 是否異常導致重試
Throwable exception = attempt.getExceptionCause(); // 執行的異常
log.info("異常:{}", exception);
}
if(attempt.hasResult()){ // 是否有返回
V result = attempt.getResult();
log.info("返回:{}",result);
}
}
})
// 控制每次重試間隔時間,如果AttemptTimeLimiter設置多線程
.withWaitStrategy(WaitStrategies.fixedWait(3,TimeUnit.SECONDS)) // 等待策略
.withBlockStrategy(BlockStrategies.threadSleepStrategy()) // 阻塞策略
//
.withStopStrategy(StopStrategies.stopAfterAttempt(5)) // 停止策略
.build();
使用Retryer讓業務代碼擁有重試能力
前兩次執行時模擬返回false,則會執行重試;當第3次時,正常執行業務代碼并返回true,結束重試
@Test
public void retryWhenResult() throws ExecutionException, RetryException {
retryer.call(() - > {
if(counter.incrementAndGet() == 3){// 模擬前2此返回false,觸發重試
log.info(" 執行業務代碼:{}次",counter.get());
return true;
}
return false;
});
}
模擬前3次出現異常,則會執行重試;當第3次時,正常執行業務代碼,結束重試
@Test
public void retryWhenException() throws ExecutionException, RetryException {
retryer.call(() - > {
if( counter.getAndIncrement() == 3 ){// 模擬前5此出現異常,觸發重試
return counter;
}
log.info(" 執行業務代碼: {}次", counter.get());
throw new RuntimeException("ERROR");
});
}
模擬前5此出現異常,由于Retryer配置重試次數為5,則最終業務代碼不會執行
@Test
public void retryWhenResultOnFailure() throws ExecutionException, RetryException {
retryer.call(() - > {
if(counter.incrementAndGet() == 8){// 模擬前7此返回false,由于配置重試5次,因此最終失敗
log.info(" 執行業務代碼:{}次",counter.get());
return true;
}
return false;
});
}
執行流程
執行流程
通過RetryerBuilder構建Retryer,調用Retryer#call,封裝業務代碼為其回調函數
- 開始循環執行
- 由AttemptTimeLimiter#call執行回調函數
- 將結果封裝為Attempt,包括兩種類型ResultAttempt,ExceptionAttempt。如果成功,記錄執行結果、持續時長;如果失敗,記錄異常、持續時長
- 執行監聽RetyrListener#onRetry,可以配置多個監聽
- 執行拒絕斷言Predicate,根據返回值、執行異常、返回異常類型判斷是否終止重試
- 如果滿足條件,則繼續重試;否則結束重試,并返回Attempt包含回調結果
- 根據終止策略StopStrategy判斷是否終止重試
- 根據等待策略WaitStrategy獲取等待時長
- 根據阻塞策略BlockStrategy與上一步等待時長阻塞重試,如果出現異常則拋出RetryException
- 重復執行以上邏輯
配置
構建Retryer主要通過RetryerBuilder.newBuilder()實現,其相關配置如下:
配置 | 策略 | 名稱 | 描述 |
---|---|---|---|
AttemptTimeLimiters | 任務執行時長限制 | ||
NoAttemptTimeLimit | 無時長限制 | ||
FixedAttemptTimeLimit | 固定時長限制 | ||
WaitStrategies | 重試等待策略 | ||
ExponentialWaitStrategy | 指數等待策略 | 按指數增加重試間隔時長,比如第一次2^1100、2^2100、2^3*100...最多300000 | |
FibonacciWaitStrategy | 斐波那契等待策略 | 1100、1100、2100、3100、5*100... | |
FixedWaitStrategy | 固定時長等待策略 | 按配置的固定間隔時間 | |
RandomWaitStrategy | 隨機時長等待策略 | 隨機間隔時間,可以設置隨機值范圍 | |
IncrementingWaitStrategy | 遞增等待策略 | 根據配置的初始值與增量進行累加時間 | |
ExceptionWaitStrategy | 異常等待策略 | 根據異常類型指定等待時間 | |
CompositeWaitStrategy | 復合等待策略 | 可配置多個策略進行組合 | |
BlockStrategies | 阻塞策略 | 根據WaitStrategies獲取阻塞時長 | |
ThreadSleepStrategy | 線程等等策略 | 通過Thread.sleet()實現 | |
StopStrategies | 重試停止策略 | ||
NeverStopStrategy | 無限制策略 | ||
StopAfterAttemptStrategy | 限定次數策略 | ||
StopAfterDelayStrategy | 限定時長策略 | ||
NoAttemptTimeLimit | 限定次數 |
注意
- AttemptTimeLimiter中的FixedAttemptTimeLimit依賴于guava中的SimpleTimeLimiter,但是在guava高版本中該類已經成了私有類
總結
Guava Retrying模塊能夠通過簡單的將代碼實現業務邏輯重試的功能,并且其配置中包含了重試的次數、時長控制、重試阻塞、終止策略等等, 在項目中是非常常用的一項技術。
-
模塊
+關注
關注
7文章
2696瀏覽量
47434 -
函數
+關注
關注
3文章
4329瀏覽量
62575 -
代碼
+關注
關注
30文章
4780瀏覽量
68535
發布評論請先 登錄
相關推薦
評論