使用场景

在日常开发中,我们经常会遇到需要调用外部服务和接口的场景。外部服务对于调用者来说一般都是不可靠的,尤其是在网络环境比较差的情况下,网络抖动很容易导致请求超时等异常情况,这时候就需要使用失败重试策略重新调用 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-retrying 的使用方式:

  • 如果抛出 IOException 则重试,如果返回结果为 null 或者等于 2 则重试,固定等待时长为 300 ms,最多尝试 3 次;

Callable<Integer> task = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {return2;}
};Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder().retryIfResult(Predicates.<Integer>isNull()).retryIfResult(Predicates.equalTo(2)).retryIfExceptionOfType(IOException.class).withStopStrategy(StopStrategies.stopAfterAttempt(3)).withWaitStrategy(WaitStrategies.fixedWait(300, TimeUnit.MILLISECONDS)).build();
try {retryer.call(task);
} catch (ExecutionException e) {e.printStackTrace();
} catch (RetryException e) {e.printStackTrace();
}
  • 出现异常则执行重试,每次任务执行最长执行时间限定为 3 s,重试间隔时间初始为 3 s,最多重试 1 分钟,随着重试次数的增加每次递增 1 s,每次重试失败,打印日志;

@Overridepublic Integer call() throws Exception {return2;}
};Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder().retryIfException().withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS)).withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS,1,TimeUnit.SECONDS)).withAttemptTimeLimiter(AttemptTimeLimiters.<Integer>fixedTimeLimit(3,TimeUnit.SECONDS)).withRetryListener(new RetryListener() {@Overridepublic <V> void onRetry(Attempt<V> attempt) {if (attempt.hasException()){attempt.getExceptionCause().printStackTrace();}}}).build();
try {retryer.call(task);
} catch (ExecutionException e) {e.printStackTrace();
} catch (RetryException e) {e.printStackTrace();
}

核心执行逻辑

long startTime = System.nanoTime();
for (int attemptNumber = 1; ; attemptNumber++) {Attempt<V> attempt;try {// 执行成功V result = attemptTimeLimiter.call(callable);attempt = new ResultAttempt<V>(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));} catch (Throwable t) {// 执行失败attempt = new ExceptionAttempt<V>(t, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));}// 监听器处理for (RetryListener listener : listeners) {listener.onRetry(attempt);}// 是否符合终止策略if (!rejectionPredicate.apply(attempt)) {return attempt.get();}// 是否符合停止策略if (stopStrategy.shouldStop(attempt)) {thrownew RetryException(attemptNumber, attempt);} else {// 计算下次重试间隔时间long sleepTime = waitStrategy.computeSleepTime(attempt);try {blockStrategy.block(sleepTime);} catch (InterruptedException e) {Thread.currentThread().interrupt();thrownew RetryException(attemptNumber, attempt);}}
}

依赖引入

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

默认的guava中也有包含。

主要接口介绍

  • Attempt:一次执行任务;

  • AttemptTimeLimiter:单次任务执行时间限制(如果单次任务执行超时,则终止执行当前任务);

  • BlockStrategies:任务阻塞策略(通俗的讲就是当前任务执行完,下次任务还没开始这段时间做什么……),默认策略为:BlockStrategies.THREAD_SLEEP_STRATEGY 也就是调用 Thread.sleep(sleepTime);

  • RetryException:重试异常;

  • RetryListener:自定义重试监听器,可以用于异步记录错误日志;

  • StopStrategy:停止重试策略,提供三种:

    • StopAfterDelayStrategy :设定一个最长允许的执行时间;比如设定最长执行10s,无论任务执行次数,只要重试的时候超出了最长时间,则任务终止,并返回重试异常RetryException;

    • NeverStopStrategy :不停止,用于需要一直轮训知道返回期望结果的情况;

    • StopAfterAttemptStrategy :设定最大重试次数,如果超出最大重试次数则停止重试,并返回重试异常;

  • WaitStrategy:等待时长策略(控制时间间隔),返回结果为下次执行时长:

    • FixedWaitStrategy:固定等待时长策略;

    • RandomWaitStrategy:随机等待时长策略(可以提供一个最小和最大时长,等待时长为其区间随机值)

    • IncrementingWaitStrategy:递增等待时长策略(提供一个初始值和步长,等待时间随重试次数增加而增加)

    • ExponentialWaitStrategy:指数等待时长策略;

    • FibonacciWaitStrategy :Fibonacci 等待时长策略;

    • ExceptionWaitStrategy :异常时长等待策略;

    • CompositeWaitStrategy :复合时长等待策略;

这或许是实现重试最优雅的姿势了!相关推荐

  1. Python 以优雅的姿势 操作文件

    Python 以优雅的姿势 操作文件 文章目录 Python 以优雅的姿势 操作文件 open() 方法

  2. 线程可以kill吗_还在用 kill -9 停机?这才是最优雅的姿势

    来源公众号Kirito的技术分享 , 作者徐靖峰 最近瞥了一眼项目的重启脚本,发现运维一直在使用 kill-9 的方式重启 springboot embedded tomcat,其实大家几乎一致认为: ...

  3. Spring Boot如何以优雅的姿势校验参数

    1.美图 2. 参考 参考:添加链接描述

  4. 贴福字、集五福、沾福气!这才是“中国福“的最优雅打开姿势

    马上就要过年啦,家里的"福"字准备好了么? 贴福字是过年的必备项目,倒着贴有一层"福到了"的寓意."福"这个字还频繁的出现在各种" ...

  5. 使用Guava retryer优雅的实现接口重试机制

    转载自: 使用Guava retrying优雅的实现接口重调机制 Guava retrying:基于 guava 的重试组件 实际项目中,为了考虑网络抖动,加锁并发冲突等场景,我们经常需要对异常操作进 ...

  6. .NET CORE编写控制台程序应有的优雅姿势(转载)

    原文地址:https://www.cnblogs.com/zuowj/p/11107243.html 本文所说的编写控制台程序应有的"正确"方法,我把正确二字加上引号,因为没有绝对 ...

  7. ActivityManagerService解读之Activity启动时间闲聊--优雅的优化我们应用的启动时间

    ActivityManagerService解读之Activity启动时间闲聊--Android Framework层时间计算介绍一文从Android Framework角度杂谈了一波应用启动时系统各 ...

  8. python3api_python3 api 中文

    如何在 Apache Flink 中使用 Python API? 作者:孙金城(金竹)整理:韩非 本文根据 Apache Flink 系列直播课程整理而成,由 Apache Flink PMC,阿里巴 ...

  9. python api中文版_python api中文

    如何在 Apache Flink 中使用 Python API? 作者:孙金城(金竹)整理:韩非 本文根据 Apache Flink 系列直播课程整理而成,由 Apache Flink PMC,阿里巴 ...

最新文章

  1. 【OpenCV】OpenCV中积分图函数与应用
  2. spring 框架学习(一)
  3. python爬虫 发送定时气象预报
  4. 团队作业4——第一次项目冲刺(Alpha版本)-第一篇
  5. mysql qps如何查看_mysql状态查看 QPS/TPS/缓存命中率查看
  6. impala sql清单
  7. Windows驱动程序运行时函数的调用
  8. html内置时间对象,JavaScript中的常用事件,以及内置对象详解
  9. 小程序资源服务器,开发小程序没有服务器资源
  10. JavaScript声明变量详解
  11. Python+OpenCV:Canny边缘检测
  12. C++-实现matlab的meshgird(OpenCV)
  13. linux 0.11 源码学习(十四)
  14. win7的附件计算机没了,win7系统附件工具不见了的解决方法
  15. win7 企业版MAK神key win7企业版激活码
  16. 湘源里面关于缩放的问题
  17. poj 3268 Silver Cow Party(最短路dijkstra)
  18. Photoshop 技能167个 经典的Photoshop技巧大全
  19. Mysql中使用mybatis中sql语句写法操作
  20. 【图像增强】基于matlab Frangi滤波器血管图像增强【含Matlab源码 2108期】

热门文章

  1. 【模板】最小割树(Gomory-Hu Tree)
  2. 安全篇之手机数字密码九宫格究竟哪个更安全?
  3. 2018-2019 Exp2 后门原理与实践
  4. WIN10系统触摸板快捷键
  5. shell中if 变量里包含字符串的判断
  6. GridView 用 checkbox 全选并取值
  7. C# 数组与 list 互相转换案例
  8. 五个最不流行的桌面环境
  9. 利用 SIFT 实现图像拼接
  10. 解决ubuntu(16.04版本)和windows电脑之间无法复制粘贴问题