前言
在日常开发中,我们经常会遇到需要调用外部服务和接口的场景。外部服务对于调用者来说一般都是不可靠的,尤其是在网络环境比较差的情况下,网络抖动很容易导致请求超时等异常情况,这时候就需要使用失败重试策略重新调用 API 接口来获取。重试策略在服务治理方面也有很广泛的使用,通过定时检测,来查看服务是否存活(Active)。
Guava Retrying是一个灵活方便的重试组件,包含了多种的重试策略,而且扩展起来非常容易。
用作者的话来说:
This is a small extension to Google’s Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.
使用Guava-retrying你可以自定义来执行重试,同时也可以监控每次重试的结果和行为,最重要的基于 Guava 风格的重试方式真的很方便。
代码
引入Guava-retry
com.github.rholder guava-retrying 2.0.0
Retryer配置
Retryer<Boolean> retryer = RetryerBuilder .<Boolean>newBuilder() //抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。 .retryIfException() //抛出连接异常才会重试 .retryIfExceptionOfType(ConnectException.class) //返回指定结果<泛型>也需要重试 .retryIfResult(Predicates.equalTo(null)) //重调策略 .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS)) //尝试次数 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) .build();
简单三步就能使用Guava Retryer优雅的实现重调方法。
接下来对其进行详细说明:
RetryerBuilder
是一个factory创建者,可以定制设置重试源且可以支持多个重试源,可以配置重试次数或重试超时时间,以及可以配置等待时间间隔,创建重试者Retryer实例。RetryerBuilder
的重试源支持Exception异常对象 和自定义断言对象,通过retryIfException
和retryIfResult
设置,同时支持多个且能兼容。retryIfException
,抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。retryIfRuntimeException
只会在抛runtime异常的时候才重试,checked异常和error都不重试。retryIfExceptionOfType
允许我们只在发生特定异常的时候才重试,比如NullPointerException和
IllegalStateException`都属于runtime异常,也包括自定义的error
重试成功如何获取返回值呢?
网上很多文章都是各种抄,自己翻源码找到了。
首先再Retryer的核心执行方法call中,发现Callable是带泛型的,同时Callable又是带返回值的线程。
看了有get(),于是继续往下翻,发现实现了接口的ResultAttempt中,有个泛型保存执行结果。
因此,如果想要重试成功返回值,以字符串为例。
Retryer<String> retryer = RetryerBuilder .<String>newBuilder() //抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。 .retryIfException() //抛出连接异常才会重试 .retryIfExceptionOfType(ConnectException.class) //返回指定结果<泛型>也需要重试 .retryIfResult(Predicates.equalTo(null)) //重调策略 .withWaitStrategy(WaitStrategies.fixedWait(2, TimeUnit.SECONDS)) //尝试次数 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) .build();
定义实现Callable接口的方法,以便Guava retryer能够调用
场景,我这里我访问一个http接口,因为是挂代理去访问的,所以存在着ip代理连接超时的异常,这时候就要进行重试。
访问接口成功就返回对应字符串,当然,自己可以根据场景定义所需的返回值。
private static Callable<String> updateReimAgentsCall = new Callable<String>() { @Override public String call() throws Exception { CrawlerService service = new CrawlerService(); try { List<PopularRoutesInfo> infoList = service.getPopularRoutes(IP.builder().host("xxxx").port(xxx).build()); if (CollectionUtils.isEmpty(infoList)) { System.out.println("抓取结果为空,继续重试..."); return null; } } catch (ConnectException e) { System.out.println("连接失败,继续重试..."); return null; } catch (Exception e) { System.out.println("连接失败,继续重试..."); return null; } return "成功抓取热门航线报价"; } };
执行任务
try { System.out.println(retryer.call(updateReimAgentsCall)); } catch (ExecutionException e) { // } catch (RetryException e) { // }
执行结果
成功时
失败时
我们主要关注下可能的异常:
RetryException和ExecutionException。
ExecutionException产生:传入的Callable执行过程中产生了异常,但是我们在构建Retryer对象的时候并没有考虑这种情况,就会抛出这个异常。抛出这异常,也就意味着重试终止。
RetryException就很简单了,当所有重试介绍后,依然不能成功,那么就会抛这异常。
封装工具类
我封装了一下,方便使用
/** * @Author: shouliang.wang * @Date: 2018/10/31 15:19 * @Description: 重试机制 */ public class RetryerUtils { /** * 获取连接异常重试机制 * @param sleepTime 重试间隔休眠时间 * @param attemptNumber 重试次数 * @param c 重试异常 * @return */ public staticRetryer getRetryer(long sleepTime, int attemptNumber,Class c) { return RetryerBuilder . newBuilder() //设置抛出指定异常重试 .retryIfExceptionOfType(c) //返回null也需要重试 .retryIfResult(Predicates.equalTo(null)) //重调策略 .withWaitStrategy(WaitStrategies.fixedWait(sleepTime, TimeUnit.MILLISECONDS)) //尝试次数 .withStopStrategy(StopStrategies.stopAfterAttempt(attemptNumber)) .build(); } }
使用示范
//重试机制 Retryer<String> retryer = RetryerUtils.getRetryer(2000, 3, ConnectException.class);
资料参考 https://juejin.im/post/5b1779bbf265da6e410e0bbb https://blog.csdn.net/aitangyong/article/details/53894997
本文由 SAn 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2020/05/14 21:18