API遠程接口在調用時會偶發網絡超時、網絡異常,導致調用失敗,這時候某些特殊需求可能需要使用重試機制,當發生網絡等異常時重新再發起調用請求。Github Retryer能完美的解決這一需求。
下面讓我們看下如何使用Github Retryer。
1. 引入GitHub Retryer依賴
<dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency>
jar包下載地址:https://maven.ityuan.com/maven2/com/github/rholder/guava-retrying/2.0.0
2. 根據調用返回接口判斷是否需要重試
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import com.github.rholder.retry.RetryException; import com.github.rholder.retry.Retryer; import com.github.rholder.retry.RetryerBuilder; import com.github.rholder.retry.StopStrategies; import com.github.rholder.retry.WaitStrategies; import com.google.common.base.Predicates; /** * @description: GitHub Retryer框架重試機制的使用 * @author ityuan.com * @date 2019年6月26日 上午11:07:38 */ public class RetryTester { public static void main(String[] args) throws ExecutionException, RetryException { Retryer<Long> retryer = RetryerBuilder.<Long>newBuilder() // 返回false也需要重試 .retryIfResult(Predicates.equalTo(1L)) // 重調策略 .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS)) // 嘗試次數 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) .build(); retryer.call(new Callable<Long>() { @Override public Long call() throws Exception { System.out.println("返回值是0L,看我能出現幾次"); return 1L; } } ); } }
執行結果
返回值是0L,看我能出現幾次 返回值是0L,看我能出現幾次 返回值是0L,看我能出現幾次 Exception in thread "main" com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 3 attempts. at com.github.rholder.retry.Retryer.call(Retryer.java:120) at com.test.RetryTester.main(RetryTester.java:31)
3.根據調用發生異常判斷是否需要重試
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import com.github.rholder.retry.RetryException; import com.github.rholder.retry.Retryer; import com.github.rholder.retry.RetryerBuilder; import com.github.rholder.retry.StopStrategies; /** * @description: GitHub Retryer框架重試機制的使用 * @author ityuan.com * @date 2019年6月26日 上午11:07:38 */ public class RetryTester2 { public static void main(String[] args) throws ExecutionException, RetryException { Retryer<Long> retryer = RetryerBuilder.<Long>newBuilder() .retryIfException() .withStopStrategy(StopStrategies.stopAfterAttempt(2)) // 重試2次后停止 .build(); retryer.call(new Callable<Long>() { @Override public Long call() throws Exception { System.out.println("異常打印,看我能出現幾次"); throw new RuntimeException(); } } ); } }
執行結果
異常打印,看我能出現幾次 異常打印,看我能出現幾次 Exception in thread "main" com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 2 attempts. at com.github.rholder.retry.Retryer.call(Retryer.java:120) at com.test.RetryTester2.main(RetryTester2.java:24) Caused by: java.lang.RuntimeException at com.test.RetryTester2$1.call(RetryTester2.java:28) at com.test.RetryTester2$1.call(RetryTester2.java:1) at com.github.rholder.retry.AttemptTimeLimiters$NoAttemptTimeLimit.call(AttemptTimeLimiters.java:78) at com.github.rholder.retry.Retryer.call(Retryer.java:110) ... 1 more
4.添加異常監聽
.withRetryListener(new MyRetryListener<>())
監聽代碼
import com.github.rholder.retry.Attempt; import com.github.rholder.retry.RetryListener; import java.util.concurrent.ExecutionException; @SuppressWarnings("hiding") public class MyRetryListener<Long> implements RetryListener { @Override public <Long> void onRetry(Attempt<Long> attempt) { // 第幾次重試,(注意:第一次重試其實是第一次調用) System.out.print("[retry]time=" + attempt.getAttemptNumber()); // 距離第一次重試的延遲 System.out.print(",delay=" + attempt.getDelaySinceFirstAttempt()); // 重試結果: 是異常終止, 還是正常返回 System.out.print(",hasException=" + attempt.hasException()); System.out.print(",hasResult=" + attempt.hasResult()); // 是什么原因導致異常 if (attempt.hasException()) { System.out.print(",causeBy=" + attempt.getExceptionCause().toString()); } else { // 正常返回時的結果 System.out.print(",result=" + attempt.getResult()); } // bad practice: 增加了額外的異常處理代碼 try { Long result = attempt.get(); System.out.print(",rude get=" + result); } catch (ExecutionException e) { System.err.println("this attempt produce exception." + e.getCause().toString()); } System.out.println(); } }
總結
RetryerBuilder是一個factory創建者,可以定制設置重試源且可以支持多個重試源,可以配置重試次數或重試超時時間,以及可以配置等待時間間隔,創建重試者Retryer實例。
RetryerBuilder的重試源支持Exception異常對象 和自定義斷言對象,通過retryIfException 和retryIfResult設置,同時支持多個且能兼容。
retryIfException,拋出runtime異常、checked異常時都會重試,但是拋出error不會重試。
retryIfRuntimeException只會在拋runtime異常的時候才重試,checked異常和error都不重試。
retryIfExceptionOfType允許我們只在發生特定異常的時候才重試,比如NullPointerException和IllegalStateException都屬於runtime異常,也包括自定義的error