重試工具庫一: Guava-Retrying

在我們的開發中,api 接口調用異常是經常會遇到的,任何接口都會有不同概率的異常情況,對於可以重入的接口,爲了避免偶發性異常造成的服務的不可用,重試機制就非常有必要了.Guava-Retryiny 是一個非常靈活的重試組件,包含多種重試策略,擴展很方便。

一、maven依賴

<dependency>
	<groupId>com.github.rholder</groupId>
	<artifactId>guava-retrying</artifactId>
	<version>2.0.0</version>
</dependency>

在這個包中,最重要的類就是 Retryer,只要我們通過一定的策略創建出 Retryer 對象,通過調用 Retryer 對象的 call 方法,即可按照既定策略執行參數中傳入的 Callable 接口的 call 方法。這裏重試的方法需要包裝到Callable接口當中, 方法很多的話使用起來稍有不便,優化方式可以使用AOP的方式來使用Retry。

二、Retryer 介紹

Retryer 提供了構造方法,用來創建一個指定規則的 Retryer:這個方法可以通過傳入嘗試時間策略、停止重試策略、重試間隔等待策略、重試阻塞策略、拒絕策略等策略來指定一個請求的重試如何進行

Retryer(@Nonnull AttemptTimeLimiter<V> attemptTimeLimiter,
		@Nonnull StopStrategy stopStrategy,
		@Nonnull WaitStrategy waitStrategy,
		@Nonnull BlockStrategy blockStrategy,
		@Nonnull Predicate<Attempt<V>> rejectionPredicate,
		@Nonnull Collection<RetryListener> listeners)

 

  1. attemptTimeLimiter -- 每次重試的最大超時,超過該超時則拋出 TimeoutException
  2. stopStrategy -- 停止重試的策略
  3. waitStrategy -- 重試間間隔等待策略
  4. blockStrategy -- 重試間間隔線程阻塞策略
  5. rejectionPredicate -- 拒絕嘗試斷言
  6. listeners -- 重試的監聽者

超時時長控制 -- AttemptTimeLimiter 接口

public interface AttemptTimeLimiter<V> {

V call(Callable<V> var1) throws Exception;

}

這個接口主要是用來控制每次重試的超時時間

通常來說,我們會通過 AttemptTimeLimiters 類來創建這個接口的實現

AttemptTimeLimiters

AttemptTimeLimiters 就是一個生產 AttemptTimeLimiter 實現的工廠類,主要的方法有三個:

不限制時間 -- noTimeLimit

 

public static <V> AttemptTimeLimiter<V> noTimeLimit()

限制超時 -- AttemptTimeLimiter

public static <V> AttemptTimeLimiter<V> fixedTimeLimit(long duration,

    @Nonnull TimeUnit timeUnit)

public static <V> AttemptTimeLimiter<V> fixedTimeLimit(long duration,

    @Nonnull TimeUnit timeUnit, @Nonnull ExecutorService executorService)

兩個方法通過 duration 和 timeUnit 組合實現了對調用的接口實現總的超時控制

 

終止策略 -- StopStrategy接口

public interface StopStrategy {
    boolean shouldStop(Attempt var1);
}

這個接口中只有一個方法,顧名思義,他就是用來判斷是否應該停止重試

他傳入了 Attempt 爲參數,通過這個參數,我們可以獲取到方法的返回值、拋出的異常、重試的次數等等

Attempt 類

public interface Attempt<V> {
V get() throws ExecutionException;
boolean hasResult();
boolean hasException();
V getResult() throws IllegalStateException;
Throwable getExceptionCause() throws IllegalStateException;
long getAttemptNumber();
long getDelaySinceFirstAttempt();

}

StopStrategys

我們也可以通過 StopStrategys 來生產 StopStrategy 對象它提供了三個 static 方法:

不停止 -- neverStop

public static StopStrategy neverStop()

在一定次數後停止 -- stopAfterAttempt

public static StopStrategy stopAfterAttempt(int attemptNumber)

在一定超時後停止 -- stopAfterDelay

public static StopStrategy stopAfterDelay(long duration, @Nonnull TimeUnit timeUnit)

 

間隔策略 -- WaitStrategy 接口

public interface WaitStrategy {

long computeSleepTime(Attempt var1);

}

WaitStrategy 接口只包含一個方法,顧名思義,就是間隔的時長

 

WaitStrategies

同樣的,我們也可以使用 WaitStrategies 類來生產 WaitStrategy,WaitStrategies 提供了非常強大而豐富的 static 方法

 

不間隔 -- noWait

public static WaitStrategy noWait()

 

指定時間間隔 -- fixedWait

public static WaitStrategy fixedWait(long sleepTime, @Nonnull TimeUnit timeUnit)

 

隨機間隔 -- randomWait

public static WaitStrategy randomWait(long maximumTime, @Nonnull TimeUnit timeUnit);

public static WaitStrategy randomWait(long minimumTime, @Nonnull TimeUnit

    minimumTimeUnit, long maximumTime, @Nonnull TimeUnit maximumTimeUnit);

 

線性遞增間隔 -- incrementingWait

public static WaitStrategy incrementingWait(long initialSleepTime,

    @Nonnull TimeUnit initialSleepTimeUnit, long increment, @Nonnull

    TimeUnit incrementTimeUnit)

這個方法設置了初始間隔和遞增步長

指數遞增間隔

public static WaitStrategy exponentialWait();

public static WaitStrategy exponentialWait(long maximumTime, @Nonnull

    TimeUnit maximumTimeUnit);

public static WaitStrategy exponentialWait(long multiplier, long

    maximumTime, @Nonnull TimeUnit maximumTimeUnit);

斐波那切數列遞增間隔 -- fibonacciWait

public static WaitStrategy fibonacciWait();

public static WaitStrategy fibonacciWait(long maximumTime, @Nonnull

    TimeUnit maximumTimeUnit);

public static WaitStrategy fibonacciWait(long multiplier, long maximumTime

    , @Nonnull TimeUnit maximumTimeUnit);

 

一旦拋出異常則間隔 -- exceptionWait

public static <T extends Throwable> WaitStrategy exceptionWait(@Nonnull

    Class<T> exceptionClass, @Nonnull Function<T, Long> function)

他是 WaitStrategies 中最複雜的一個策略了,一旦上一次嘗試拋出了 exceptionClass 及其子類的異常,則調用回調方法 function,以其返回值作爲下一次嘗試前的時間間隔

 

合併多個策略 -- join

public static WaitStrategy join(WaitStrategy... waitStrategies)

 

阻塞策略 -- BlockStrategy

public interface BlockStrategy {

void block(long var1) throws InterruptedException;

}

這個策略指定了線程在本次嘗試後 sleep 多少毫秒

 

BlockStrategies

BlockStrategies 可以方便的創建 BlockStrategy

他只提供了一個 static 方法,一旦指定,則線程會在間隔時間內 sleep,否則不會

public static BlockStrategy threadSleepStrategy()

 

斷言 -- Predicate 接口

@FunctionalInterface

@GwtCompatible

public interface Predicate<T> extends java.util.function.Predicate<T> {

@CanIgnoreReturnValue

boolean apply(@Nullable T var1);

boolean equals(@Nullable Object var1);

default boolean test(@Nullable T input) {

return this.apply(input);

}

}

 

Predicate 接口最重要的方法是 apply 方法,返回是否需要拒絕嘗試,與 AttemptTimeLimiters 類似,Predicate 通常用 Predicates 來創建

Predicates

Predicates 提供了非常豐富的 static 方法集合,可以實現各種各樣的斷言,甚至是斷言的與或非組合

public static <T> Predicate<T> alwaysFalse();

public static <T> Predicate<T> isNull();

public static <T> Predicate<T> notNull();

public static <T> Predicate<T> not(Predicate<T> predicate);

public static <T> Predicate<T> and(Iterable<? extends Predicate<? super T

    >> components);

public static <T> Predicate<T> and(Predicate... components);

public static <T> Predicate<T> and(Predicate<? super T> first, Predicate

    <? super T> second);

public static <T> Predicate<T> or(Iterable<? extends Predicate<? super T

    >> components);

public static <T> Predicate<T> or(Predicate... components);

public static <T> Predicate<T> or(Predicate<? super T> first, Predicate<?

    super T> second);

public static <T> Predicate<T> equalTo(@NullableDecl T target);

public static Predicate<Object> instanceOf(Class<?> clazz);

public static Predicate<Class<?>> subtypeOf(Class<?> clazz);

public static <T> Predicate<T> in(Collection<? extends T> target);

public static <A, B> Predicate<A> compose(Predicate<B> predicate,

    Function<A, ? extends B> function);

public static Predicate<CharSequence> containsPattern(String pattern);

public static Predicate<CharSequence> contains(Pattern pattern);

 

讓線程池中的線程也擁有重試功能 -- wrap

上面提到,Retryer 具有 call 方法,只要設置好重試策略,將相應方法封裝爲 Callable 接口的實現,傳入 Retryer 的 call 方法,即可按照預定的重試策略調用對應的方法

但是,如果我們的方法不是直接執行,而是需要放入線程池中呢?Retryer 提供了 wrap 接口實現將方法的重試策略封裝到一個 Callable 實現中,從而讓我們可以直接通過線程池調用:

public Retryer.RetryerCallable<V> wrap(Callable<V> callable)

 

 

三、Retryer 創建工具 -- RetryerBuilder

由於 Retryer 類構造方法參數較多,較爲複雜,而使用 RetryerBuilder 要更加簡潔明瞭,也是更加常用的方式.如下:

  1.  Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                    // 如果Callable結果爲null,繼續重試
                    .retryIfResult(Predicates.<Boolean>isNull())
                    // IOException,繼續重試
                    .retryIfExceptionOfType(IOException.class)
                    // RuntimeException,繼續重試
                    .retryIfRuntimeException()
                    // 指定重試策略,重試三次後停止
                    .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                    .build();

     

發佈了44 篇原創文章 · 獲贊 13 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章