一、使用場(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)等待策略;