guava-retrying重試工具庫: 隔多長時間重試

guava-retrying提供了WaitStrategy接口,用來控制2次重試的時間間隔,這個接口與StopStrategy有的類似。內置的等待策略在WaitStrategies中定義。


import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;

public class AlwaysExceptionTask implements Callable<Boolean> {

    private static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss,SSS");
    private int times = 1;

    @Override
    public Boolean call() throws Exception {
        System.out.println(df.format(new Date()));
        int thisTimes = times;
        times++;

        if (thisTimes == 1) {
            throw new NullPointerException();
        } else if (thisTimes == 2) {
            throw new IOException();
        } else if (thisTimes == 3) {
            throw new ArithmeticException();
        } else {
            throw new Exception();
        }
    }
}


WaitStrategies.noWait()失敗後立刻重試,沒有等待時間。

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
		.retryIfException()
		.withWaitStrategy(WaitStrategies.noWait())
		.build();

System.out.println("begin..." + df.format(new Date()));

try {
	retryer.call(new AlwaysExceptionTask());
} catch (Exception e) {
	System.err.println("still failed after retry." + e.getCause().toString());
}

System.out.println("end..." + df.format(new Date()));


WaitStrategies.fixedWait(1, TimeUnit.SECONDS)間隔固定時間之後重試,比如每隔1s重試一次。

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
		.retryIfException()
		.withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
		.build();

System.out.println("begin..." + df.format(new Date()));

try {
	retryer.call(new AlwaysExceptionTask());
} catch (Exception e) {
	System.err.println("still failed after retry." + e.getCause().toString());
}


WaitStrategies.randomWait(3, TimeUnit.SECONDS)間隔隨機時間後重試,比如間隔0~3中隨機時間後重試。

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
		.retryIfException()
		.withWaitStrategy(WaitStrategies.randomWait(3, TimeUnit.SECONDS))
		.build();

System.out.println("begin..." + df.format(new Date()));

try {
	retryer.call(new AlwaysExceptionTask());
} catch (Exception e) {
	System.err.println("still failed after retry." + e.getCause().toString());
}

System.out.println("end..." + df.format(new Date()));


WaitStrategies.randomWait(2, TimeUnit.SECONDS, 5, TimeUnit.SECONDS)最小值,最大值之間的隨機時間。

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
		.retryIfException()
		.withWaitStrategy(WaitStrategies.randomWait(2, TimeUnit.SECONDS, 5, TimeUnit.SECONDS))
		.build();

System.out.println("begin..." + df.format(new Date()));

try {
	retryer.call(new AlwaysExceptionTask());
} catch (Exception e) {
	System.err.println("still failed after retry." + e.getCause().toString());
}

System.out.println("end..." + df.format(new Date()));




WaitStrategies.incrementingWait增量重試,重試的次數越多,等待時間間隔越長。incrementingWait需要傳遞2個參數,一個是initialSleepTime(第一次到第二次嘗試的間隔),一個是increment(每增加一次嘗試,需要增加的時間間隔)。第一次嘗試是不需要等待的,因爲guava-retrying中的第一次嘗試,對應正常的第一次調用。從第二次重試開始,第n-1次到n次間隔是:initialSleepTime + (n-2)*increment。

1、WaitStrategies.incrementingWait(0, TimeUnit.SECONDS, 0, TimeUnit.SECONDS)等價於     WaitStrategies.noWait()。

2、 WaitStrategies.incrementingWait(1, TimeUnit.SECONDS, 0, TimeUnit.SECONDS)等價於WaitStrategies.fixedWait(1, TimeUnit.SECONDS)

3、 WaitStrategies.incrementingWait(0, TimeUnit.SECONDS, 1, TimeUnit.SECONDS)等價於WaitStrategies.fixedWait(1, TimeUnit.SECONDS)。


4、  WaitStrategies.incrementingWait(1, TimeUnit.SECONDS, 1, TimeUnit.SECONDS)



WaitStrategies.fibonacciWait()按照斐波那契數列等待。fibonacciWait(long multiplier,long maximumTime,TimeUnit maximumTimeUnit),multiplier單位固定是ms,maximumTime最大等待時間。n=1的時候,是無需等待的。當n>=2的時候,開始符合斐波那契數列。n=2的時候,等待1 * multiplier毫秒;n=3的時候,等待1 * multiplier毫秒;n=4的時候,等待2 * multiplier毫秒;n=5的時候,等待3 * multiplier毫秒;n=6的時候,等待5 * multiplier毫秒;n=7的時候,等待8 * multiplier毫秒;n=8的時候,等待15 * multiplier毫秒;......但是等待時間最長不超過maximumTime。

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
		.retryIfException()
		.withWaitStrategy(WaitStrategies.fibonacciWait(100, 10, TimeUnit.SECONDS))
		.build();

System.out.println("begin..." + df.format(new Date()));

try {
	retryer.call(new AlwaysExceptionTask());
} catch (Exception e) {
	System.err.println("still failed after retry." + e.getCause().toString());
}


WaitStrategies.exponentialWait按照指數遞增(2的n次方)來等待,各個參數含義與fibonacciWait相同。

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
		.retryIfException()
		.withWaitStrategy(WaitStrategies.exponentialWait(100, 10, TimeUnit.SECONDS))
		.build();

System.out.println("begin..." + df.format(new Date()));

try {
	retryer.call(new AlwaysExceptionTask());
} catch (Exception e) {
	System.err.println("still failed after retry." + e.getCause().toString());
}


WaitStrategies.exceptionWait根據拋出的異常來決定等待的時間長短,沒有什麼實際用處,不過作爲學習還是可以瞭解下,說不定將來能排上用場呢。下面我們定義1個任務,隨機拋出不同異常。

class RandomExceptionTask implements Callable<Boolean> {
	@Override
	public Boolean call() throws Exception {
		int round = (int) Math.floor(Math.random() * 8);
		System.out.println("round=" + round + ",time=" + df.format(new Date()));
		if (round == 1) {
			throw new NullPointerException();
		} else if (round == 2) {
			throw new IOException();
		} else if (round == 3) {
			throw new ArithmeticException();
		} else if (round == 4) {
			throw new IllegalStateException();
		} else if (round == 5) {
			throw new IndexOutOfBoundsException();
		} else {
			return true;
		}
	}
}


如果我們希望實現這種效果:如果出現了NullPointerException,那麼等待1s後再重試;如果拋出IOException,那等待2s後再重試;如果出現了ArithmeticException,那麼等待3s後再重試;如果出現了IllegalStateException,那麼等待4s後再重試;如果出現了IndexOutOfBoundsException,那麼等待5s後再重試;否則不等待,立刻重試。
// 根據不同異常,等待不同時間後重試
private static <T extends Throwable> Function<T, Long> itsFunction(Class<T> exceptionClass) {
	Function<T, Long> result = new Function<T, Long>() {
		@Nullable
		@Override
		public Long apply(@Nullable T input) {
			if (input instanceof NullPointerException) {
				return 1 * 1000L;
			} else if (input instanceof IOException) {
				return 2 * 1000L;
			} else if (input instanceof ArithmeticException) {
				return 3 * 1000L;
			} else if (input instanceof IllegalStateException) {
				return 4 * 1000L;
			} else if (input instanceof IndexOutOfBoundsException) {
				return 5 * 1000L;
			} else {
				return 0L;
			}
		}
	};
	return result;
}


下面是測試代碼,可以看出不同的異常確實會導致不同的重試間隔。

WaitStrategy exceptionJoin = WaitStrategies.join(
		WaitStrategies.exceptionWait(NullPointerException.class, itsFunction(NullPointerException.class)),
		WaitStrategies.exceptionWait(IOException.class, itsFunction(IOException.class)),
		WaitStrategies.exceptionWait(ArithmeticException.class, itsFunction(ArithmeticException.class)),
		WaitStrategies.exceptionWait(IllegalStateException.class, itsFunction(IllegalStateException.class)),
		WaitStrategies.exceptionWait(IndexOutOfBoundsException.class, itsFunction(IndexOutOfBoundsException.class))
		);

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
		.retryIfException()
		.withWaitStrategy(exceptionJoin)
		.build();

System.out.println("begin..." + df.format(new Date()));

try {
	retryer.call(new RandomExceptionTask());
} catch (Exception e) {
	System.err.println("still failed after retry." + e.getCause().toString());
}

System.out.println("end..." + df.format(new Date()));



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