使用場景
做自動化場景,對于一些數(shù)據(jù)統(tǒng)計祖凫,有時數(shù)據(jù)同步更新會遇到延遲的場景琼蚯,延時的時間可能不確定,此時惠况,如果用 sleep 一定時間的話遭庶,可能會造成時間的浪費。那么有什么方法了避免呢稠屠,比如使用重試機制峦睡,設(shè)置重試的時間和重試的次數(shù),當(dāng)獲取不到數(shù)據(jù)時根據(jù)預(yù)定的時間來重新請求接口权埠。
重試的實現(xiàn)的要素
- 重試觸發(fā)時機
- 什么條件去停止
- 重試等待時間
- 重試停止條件
- 接口請求時間限制
- 如果結(jié)束
- 重試時的監(jiān)聽
guava-retrying 策略介紹
- ** retryIfException() **: 當(dāng)發(fā)生任何異常時觸發(fā)重試榨了。
- ** retryIfExceptionOfType(Class<? extends Throwable> exceptionType) ** : 當(dāng)發(fā)生指定類型的異常時觸發(fā)重試。
- ** retryIfRuntimeException() **:當(dāng)發(fā)生 RuntimeException 或其子類異常時觸發(fā)重試攘蔽。
- ** retryIfResult(Predicate<R> resultPredicate) ** :根據(jù)返回結(jié)果的條件觸發(fā)重試龙屉。可以使用自定義的 Predicate 對返回結(jié)果進行判斷满俗。
- ** retryIfException(Predicate<Throwable> exceptionPredicate) **:根據(jù)異常的條件觸發(fā)重試转捕。可以使用自定義的 Predicate 對異常進行判斷漫雷。
- ** withStopStrategy(StopStrategy stopStrategy) **:配置停止策略瓜富,指定在何時停止重試。常用的停止策略包括:
- ** StopStrategies.stopAfterAttempt(int maxAttempts) **:在達到最大嘗試次數(shù)后停止重試降盹。
- ** StopStrategies.neverStop() **:永不停止重試与柑,會一直進行重試
- ** withWaitStrategy(WaitStrategy waitStrategy) **:配置等待策略谤辜,指定每次重試之間的等待時間。常用的等待策略包括:
- ** WaitStrategies.fixedWait(long sleepTime, TimeUnit timeUnit) **:固定等待時間价捧,每次重試之間等待指定的時間間隔丑念。
- ** WaitStrategies.randomWait(long minimumTime, long maximumTime, TimeUnit timeUnit) **:隨機等待時間,每次重試之間等待隨機的時間間隔结蟋。
- ** withAttemptTimeLimiter(AttemptTimeLimiter<V> attemptTimeLimiter) **:配置重試時間限制器脯倚,用于限制每次重試的時間∏妒海可以使用 FixedAttemptTimeLimit推正、ExponentialAttemptTimeLimit 或自定義的時間限制器。
- ** withRetryListener(RetryListener retryListener) **:添加重試監(jiān)聽器宝惰,用于在重試過程中觸發(fā)相應(yīng)的事件植榕。可以實現(xiàn) RetryListener 接口自定義監(jiān)聽器
- ** withBlockStrategy() **:方法用于配置阻塞策略尼夺,指定在進行重試時的阻塞行為尊残。阻塞策略定義了在重試之間是否需要進行阻塞等待,以及等待的方式和時長
構(gòu)造一個 Retryer
Retryer<T> retryer = RetryerBuilder.<T>newBuilder()
.retryIfException()
// 自定義重試條件
.retryIfExceptionOfType(NeedRetryException.class)
//停止策略淤堵,最大嘗試請求寝衫, attemptNumber次
.withStopStrategy(StopStrategies.stopAfterAttempt(attemptNumber))
//等待策略,每次請求間隔1s
.withWaitStrategy(WaitStrategies.fixedWait(sleepTime, TimeUnit.SECONDS))
//嘗試請求時間限制拐邪,不能超過 taskTime
.withAttemptTimeLimiter(new FixedAttemptTimeLimit(taskTime, TimeUnit.SECONDS, Executors.newCachedThreadPool()))
//方法用于配置阻塞策略慰毅,指定在進行重試時的阻塞行為
.withBlockStrategy(spinBlockStrategy)
//重試過程的監(jiān)聽策略,打印了嘗試的次數(shù)
.withRetryListener(new RetryListener() {
@Override
public <V> void onRetry(Attempt<V> attempt) {
log.info("嘗試次數(shù) " + attempt.getAttemptNumber());
}
})
.build();
嘗試定義一個方法庙睡,使用 Callable 接口作為參數(shù)傳入一個執(zhí)行的任務(wù)
public static <T> T retry(Callable<T> task, int attemptNumber, int sleepTime, int taskTime) throws Throwable {
SpinBlockStrategy spinBlockStrategy = new SpinBlockStrategy();
spinBlockStrategy.block(1);
Retryer<T> retryer = RetryerBuilder.<T>newBuilder()
.retryIfException()
// 自定義重試條件
.retryIfExceptionOfType(NeedRetryException.class)
//停止策略事富,最大嘗試請求, attemptNumber次
.withStopStrategy(StopStrategies.stopAfterAttempt(attemptNumber))
//等待策略乘陪,每次請求間隔1s
.withWaitStrategy(WaitStrategies.fixedWait(sleepTime, TimeUnit.SECONDS))
//嘗試請求時間限制,不能超過 taskTime
.withAttemptTimeLimiter(new FixedAttemptTimeLimit(taskTime, TimeUnit.SECONDS, Executors.newCachedThreadPool()))
//方法用于配置阻塞策略雕擂,指定在進行重試時的阻塞行為
.withBlockStrategy(spinBlockStrategy)
//重試過程的監(jiān)聽策略啡邑,打印了嘗試的次數(shù)
.withRetryListener(new RetryListener() {
@Override
public <V> void onRetry(Attempt<V> attempt) {
log.info("嘗試次數(shù) " + attempt.getAttemptNumber());
}
})
.build();
try {
return retryer.call(task);
} catch (RetryException | ExecutionException e) {
//throw inner exception
String msg = "";
log.error("retry internal error: {}", msg);
throw new RuntimeException(msg);
}
}
guava-retrying3 結(jié)合testng使用,如果當(dāng)我們Assert來作為判斷任務(wù)的嘗試條件時井赌,有一個點需要注意惋耙,retry中的 call 方法最終拋出的對象為 throw new ExecutionError((Error)cause)网棍,這邊 new 出了一個 com.google.common.util.concurrent.ExecutionError 類的對象。而不是 java.util 類的。因此硅堆,我們需要重寫重試報錯類型
代碼中call的方法
retryer.call(task)
private void wrapAndThrowRuntimeExecutionExceptionOrError(Throwable cause) {
if (cause instanceof Error) {
throw new ExecutionError((Error)cause);
} else {
throw new UncheckedExecutionException(cause);
}
}