API 接口调用异常, 网络异常在咱们平常开发中常常会遇到,这种状况下咱们须要先重试几回调用才能将其标识为错误并在确认错误以后发送异常提醒。guava-retry能够灵活的实现这一功能。Guava retryer在支持重试次数和重试频度控制基础上,可以兼容支持多个异常或者自定义实体对象的重试源定义,让重试功能有更多的灵活性。Guava Retryer也是线程安全的,入口调用逻辑采用的是Java.util.concurrent.Callable的call方法。
使用Guava retryer 很简单,咱们只要作如下几步:java
<guava-retry.version>2.0.0</guava-retry.version> <dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>${guava-retry.version}</version> </dependency>
/** * @desc 更新可代理报销人接口 * @author jianzhang11 * @date 2017/3/31 15:17 */ private static Callable<Boolean> updateReimAgentsCall = new Callable<Boolean>() { @Override public Boolean call() throws Exception { String url = ConfigureUtil.get(OaConstants.OA_REIM_AGENT); String result = HttpMethod.post(url, new ArrayList<BasicNameValuePair>()); if(StringUtils.isEmpty(result)){ throw new RemoteException("获取OA可报销代理人接口异常"); } List<OAReimAgents> oaReimAgents = JSON.parseArray(result, OAReimAgents.class); if(CollectionUtils.isNotEmpty(oaReimAgents)){ CacheUtil.put(Constants.REIM_AGENT_KEY,oaReimAgents); return true; } return false; } };
Retryer<Boolean> retryer = RetryerBuilder .<Boolean>newBuilder() //抛出runtime异常、checked异常时都会重试,可是抛出error不会重试。 .retryIfException() //返回false也须要重试 .retryIfResult(Predicates.equalTo(false)) //重调策略 .withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS)) //尝试次数 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) .build(); try { retryer.call(updateReimAgentsCall); } catch (ExecutionException e) { // e.printStackTrace(); } catch (RetryException e) { logger.error("更新可代理报销人异常,须要发送提醒邮件"); }
简单三步就能使用Guava Retryer优雅的实现重调方法。
接下来对其进行详细说明: git
RetryerBuilder
是一个factory建立者,能够定制设置重试源且能够支持多个重试源,能够配置重试次数或重试超时时间,以及能够配置等待时间间隔,建立重试者Retryer实例。RetryerBuilder
的重试源支持Exception异常对象 和自定义断言对象,经过retryIfException
和retryIfResult
设置,同时支持多个且能兼容。retryIfException
,抛出runtime异常、checked异常时都会重试,可是抛出error不会重试。retryIfRuntimeException
只会在抛runtime异常的时候才重试,checked异常和error都不重试。retryIfExceptionOfType
容许咱们只在发生特定异常的时候才重试,好比NullPointerException和
IllegalStateException`都属于runtime异常,也包括自定义的error 如:github
.retryIfExceptionOfType(Error.class)// 只在抛出error重试
固然咱们还能够在只有出现指定的异常的时候才重试,如: 安全
.retryIfExceptionOfType(IllegalStateException.class) .retryIfExceptionOfType(NullPointerException.class)
或者经过Predicate实现网络
.retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class), Predicates.instanceOf(IllegalStateException.class)))
retryIfResult能够指定你的Callable方法在返回值的时候进行重试,如 ide
// 返回false重试 .retryIfResult(Predicates.equalTo(false)) //以_error结尾才重试 .retryIfResult(Predicates.containsPattern("_error$"))
当发生重试以后,假如咱们须要作一些额外的处理动做,好比发个告警邮件啥的,那么能够使用RetryListener
。每次重试以后,guava-retrying会自动回调咱们注册的监听。能够注册多个RetryListener
,会按照注册顺序依次调用。post
import com.github.rholder.retry.Attempt; import com.github.rholder.retry.RetryListener; import java.util.concurrent.ExecutionException; public class MyRetryListener<Boolean> implements RetryListener { @Override public <Boolean> void onRetry(Attempt<Boolean> 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 { Boolean 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(); } }
接下来在Retry对象中指定监听: ui
.withRetryListener(new MyRetryListener<>())
效果以下:this