使用 guava-retrying 實(shí)現(xiàn)靈活的重試機(jī)制
有意思的是,這個項(xiàng)目最初源于 Jean-Baptiste Nizet 在 guava 倉庫下的評論。
guava-retrying 入門
<dependency><groupId>com.github.rholdergroupId><artifactId>guava-retryingartifactId><version>2.0.0version>dependency>
compile "com.github.rholder:guava-retrying:2.0.0"// 調(diào)用接口boolean result;AtomicInteger atomicInteger = new AtomicInteger(0);int sleepNum = 10000;while(!result && atomicInteger.get() < 4) {atomicInteger.incrementAndGet();result = thirdApi.invoke();Thread.sleep(sleepNum);sleepNum += sleepNum * atomicInteger.get();}
Callable<Boolean> callable = () -> {return thirdApi.invoke(); // 業(yè)務(wù)邏輯};// 定義重試器Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder().retryIfResult(Predicates.<Boolean>isNull()) // 如果結(jié)果為空則重試.retryIfExceptionOfType(IOException.class) // 發(fā)生IO異常則重試.retryIfRuntimeException() // 發(fā)生運(yùn)行時異常則重試.withWaitStrategy(WaitStrategies.incrementingWait(10, TimeUnit.SECONDS, 10, TimeUnit.SECONDS)) // 等待.withStopStrategy(StopStrategies.stopAfterAttempt(4)) // 允許執(zhí)行4次(首次執(zhí)行 + 最多重試3次).build();try {retryer.call(callable); // 執(zhí)行} catch (RetryException | ExecutionException e) { // 重試次數(shù)超過閾值或被強(qiáng)制中斷e.printStackTrace();}
重試條件 retryIfResult、retryIfExceptionOfType、retryIfRuntimeException 重試等待策略(延遲)withWaitStrategy 重試停止策略 withStopStrategy 阻塞策略、超時限制、注冊重試監(jiān)聽器(上述代碼未使用)
RetryerBuilder<V> newBuilder()Retryer<V> build()
重試條件
RetryerBuilder<V> retryIfResult(@Nonnull Predicate<V> resultPredicate)// 發(fā)生任何異常都重試retryIfException()// 發(fā)生 Runtime 異常都重試RetryerBuilderretryIfRuntimeException() // 發(fā)生指定 type 異常時重試RetryerBuilderretryIfExceptionOfType( Class extends Throwable> exceptionClass) // 匹配到指定類型異常時重試RetryerBuilderretryIfException( Predicate exceptionPredicate)
RetryerBuilder<V> withWaitStrategy(@Nonnull WaitStrategy waitStrategy) throws IllegalStateException// 參數(shù):等待時間,時間單位WaitStrategy fixedWait(long sleepTime, @Nonnull TimeUnit timeUnit) throws IllegalStateException
// 參數(shù):隨機(jī)上限,時間單位WaitStrategy randomWait(long maximumTime, @Nonnull TimeUnit timeUnit)// 參數(shù):隨機(jī)下限,下限時間單位,隨機(jī)上限,上限時間單位WaitStrategy randomWait(long minimumTime,@Nonnull TimeUnit minimumTimeUnit,long maximumTime,@Nonnull TimeUnit maximumTimeUnit)
// 參數(shù):初始等待時長,初始值時間單位,遞增值,遞增值時間單位WaitStrategy incrementingWait(long initialSleepTime,@Nonnull TimeUnit initialSleepTimeUnit,long increment,@Nonnull TimeUnit incrementTimeUnit)
// 無參數(shù)(默認(rèn)初始值為1)WaitStrategy exponentialWait()// 參數(shù):最大等待時長,最大等待時間單位(默認(rèn)初始值為1)WaitStrategy exponentialWait(long maximumTime, @Nonnull TimeUnit maximumTimeUnit)// 參數(shù):初始值,最大等待時長,最大等待時間單位WaitStrategy exponentialWait(long multiplier, long maximumTime, @Nonnull TimeUnit maximumTimeUnit)
// 無參數(shù)(默認(rèn)初始值為1)WaitStrategy fibonacciWait()// 參數(shù):最大等待時長,最大等待時間單位(默認(rèn)初始值為1)WaitStrategy fibonacciWait(long maximumTime, @Nonnull TimeUnit maximumTimeUnit)// 參數(shù):最大等待時長,最大等待時間單位(默認(rèn)初始值為1)WaitStrategy fibonacciWait(long multiplier, long maximumTime, @Nonnull TimeUnit maximumTimeUnit)
// 參數(shù):異常類型,計算等待時長的函數(shù)extends Throwable> WaitStrategy exceptionWait( ClassexceptionClass, Functionfunction)
// 參數(shù):等待策略數(shù)組WaitStrategy join(WaitStrategy... waitStrategies)
RetryerBuilder<V> withBlockStrategy(@Nonnull BlockStrategy blockStrategy) throws IllegalStateExceptionprivate static class ThreadSleepStrategy implements BlockStrategy {public void block(long sleepTime) throws InterruptedException {Thread.sleep(sleepTime);}}
停止策略
RetryerBuilder<V> withStopStrategy(@Nonnull StopStrategy stopStrategy) throws IllegalStateException超時限制
RetryerBuilder<V> withAttemptTimeLimiter(@Nonnull AttemptTimeLimiter<V> attemptTimeLimiter)NoAttemptTimeLimit:不限制執(zhí)行時間 FixedAttemptTimeLimit:限制執(zhí)行時間為固定值
監(jiān)聽器
import com.github.rholder.retry.Attempt;import com.github.rholder.retry.RetryListener;public class MyRetryListener<T> implements RetryListener {@Overridepublic <T> void onRetry(Attempt<T> attempt) {// 第幾次重試,(注意:第一次重試其實(shí)是第一次調(diào)用)System.out.print("[retry]time=" + attempt.getAttemptNumber());// 距離第一次重試的延遲System.out.print(",delay=" + attempt.getDelaySinceFirstAttempt());// 重試結(jié)果: 是異常終止, 還是正常返回System.out.print(",hasException=" + attempt.hasException());System.out.print(",hasResult=" + attempt.hasResult());// 是什么原因?qū)е庐惓?/span>if (attempt.hasException()) {System.out.print(",causeBy=" + attempt.getExceptionCause().toString());} else {// 正常返回時的結(jié)果System.out.print(",result=" + attempt.getResult());}}}
public V call(Callablecallable ) throws ExecutionException, RetryException {long startTime = System.nanoTime(); // 1. 記錄開始時間,用于后續(xù)的時間計算for (int attemptNumber = 1; ; attemptNumber++) {Attemptattempt; try {V result = attemptTimeLimiter.call(callable); // 2. 執(zhí)行callable任務(wù),得到attemptattempt = new ResultAttempt(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)); } catch (Throwable t) {attempt = new ExceptionAttempt(t, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)); }for (RetryListener listener : listeners) { // 3. 如果有***,通知listener.onRetry(attempt);}if (!rejectionPredicate.apply(attempt)) { // 4. 如果執(zhí)行callable出現(xiàn)異常,則返回異常的attemptreturn attempt.get();}if (stopStrategy.shouldStop(attempt)) { // 5. 根據(jù)停止策略判斷是否停止重試throw new RetryException(attemptNumber, attempt); // 若停止,拋出異常} else {long sleepTime = waitStrategy.computeSleepTime(attempt); // 6. 根據(jù)等待策略計算休眠時間try {blockStrategy.block(sleepTime); // 7. 根據(jù)阻塞策略決定休眠行為,默認(rèn)為sleep} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RetryException(attemptNumber, attempt);}}}}
如果 attemptTimeLimiter 是 NoAttemptTimeLimit,則直接調(diào)用 callable.call 執(zhí)行。 如果 attemptTimeLimiter 是 FixedAttemptTimeLimit,則調(diào)用 timeLimiter.callWithTimeout 限制執(zhí)行時間。
rejectionPredicate = Predicates.or(rejectionPredicate, new ExceptionClassPredicate(RuntimeException.class)); 有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
評論
圖片
表情
