一、使用场景

在日常开发中,我们经常会遇到需要调用外部服务和接口的场景。外部服务对于调用者来说一般都是不可靠的,尤其是在网络环境比较差的情况下,网络抖动很容易导致请求超时等异常情况,这时候就需要使用失败重试策略重新调用 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>() {
@Override
public Integer call() throws Exception {
return 2;
}
}; 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,每次重试失败,打印日志;
@Override
public Integer call() throws Exception {
return 2;
}
}; 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() {
@Override
public <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)) {
throw new RetryException(attemptNumber, attempt);
} else {
// 计算下次重试间隔时间
long sleepTime = waitStrategy.computeSleepTime(attempt);
try {
blockStrategy.block(sleepTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new 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 :复合时长等待策略;

【Guava】基于guava的重试组件Guava-Retryer的更多相关文章

  1. guava 学习笔记 使用瓜娃(guava)的选择和预判断使代码变得简洁

    guava 学习笔记 使用瓜娃(guava)的选择和预判断使代码变得简洁 1,本文翻译自 http://eclipsesource.com/blogs/2012/06/06/cleaner-code- ...

  2. ☕【Java技术指南】「Guava Collections」实战使用相关Guava不一般的集合框架

    Google Guava Collections 使用介绍 简介 Google Guava Collections 是一个对 Java Collections Framework 增强和扩展的一个开源 ...

  3. Guava-retry,java重试组件

    使用场景 在日常开发中,我们经常会遇到需要调用外部服务和接口的场景.外部服务对于调用者来说一般都是不可靠的,尤其是在网络环境比较差的情况下,网络抖动很容易导致请求超时等异常情况,这时候就需要使用失败重 ...

  4. 基于vue项目的组件中导入mui框架初始化滑动等效果时需移除严格模式的问题

    基于vue项目的组件中导入mui框架初始化滑动等效果时,控制台报错:Uncaught TypeError: 'caller', 'callee', and 'arguments' properties ...

  5. 基于log4net的日志组件扩展封装,实现自动记录交互日志 XYH.Log4Net.Extend(微服务监控)

    背景: 随着公司的项目不断的完善,功能越来越复杂,服务也越来越多(微服务),公司迫切需要对整个系统的每一个程序的运行情况进行监控,并且能够实现对自动记录不同服务间的程序调用的交互日志,以及通一个服务或 ...

  6. 基于第三方vuejs库组件做适配性个性开发

    相信大家在使用vuejs时候会用到很多的第三方库,能够找到适合自己的库并且加以使用可以大大加快进度,减少bug.但是很多时候会出现这样一个尴尬的境地: 基线的第三方组件并不能很好地满足我们自己地需求, ...

  7. 一个基于swoole的作业调度组件,已经实现了redis和rabitmq队列消息存储。

    https://github.com/kcloze/swoole-jobs 一个基于swoole的作业调度组件,已经实现了redis和rabitmq队列消息存储.参考资料:swoole https:/ ...

  8. 如何基于 React 封装一个组件

    如何基于 React 封装一个组件 前言 很多小伙伴在第一次尝试封装组件时会和我一样碰到许多问题,比如人家的组件会有 color 属性,我们在使用组件时传入组件文档中说明的属性值如 primary , ...

  9. Guava 教程2-深入探索 Google Guava 库

    原文出处: oschina 在这个系列的第一部分里,我简单的介绍了非常优秀的Google collections和Guava类库,并简要的解释了作为Java程序员,如果使用Guava库来减少项目中大量 ...

随机推荐

  1. Java之RandomAccessFile小结

    今天跟大家分享一下javase中的关于I/O的操作: 有时我们需要在文件的末尾追加一些内容,在这时用RandomAccessFile就很好. 这个类有两个构造方法: RandomAccessFile( ...

  2. 团体程序设计天梯赛L1-018 大笨钟 2017-03-22 17:29 79人阅读 评论(0) 收藏

    L1-018. 大笨钟 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 微博上有个自称"大笨钟V"的家伙,每 ...

  3. polymer-quick tour of polymer

    注册一个元素 <link rel="import" href="bower_components/polymer/polymer.html">//没 ...

  4. Oracle EBS应用笔记整理 (转自IT++ flyingkite)

    ***************************************************** Author: Flyingkite Blog:   http://space.itpub. ...

  5. 二段Linq Groupby操作

    var messages = list.GroupBy(p=>p.RefOrderNo,(k,v)=> new {OrderNo = k,SkuInfo = v}) .Select(p = ...

  6. MySQL简单实现多字段模糊查询

    我所做的商城项目前些时提了新需求,要求前台搜索商品除了能通过商品名称搜索到以外,还可以通过别个信息搜索,比如:商品编号.详情内容描述等等,类似于全文搜索了.我首先想到的就是lucene,但是对代码这样 ...

  7. nginx 用户登录认证

    1.配置nginx server { listen ; server_name kibana.×××.com; location / { auth_basic "secret"; ...

  8. ffmpeg学习(二) 通过rtsp获取H264裸流并保存到mp4文件

    本篇将使用上节http://www.cnblogs.com/wenjingu/p/3977015.html中编译好的库文件通过rtsp获取网络上的h264裸流并保存到mp4文件中. 1.VS2010建 ...

  9. Solr相似度名词:VSM(Vector Space Model)向量空间模型

    最近想学习下Lucene ,以前运行的Demo就感觉很神奇,什么原理呢,尤其是查找相似度最高的.最优的结果.索性就直接跳到这个问题看,很多资料都提到了VSM(Vector Space Model)即向 ...

  10. Android Performance 性能提升

    1. 经常变动的字符串要用 StringBuilder,然后每次变动用 append 方法.而不应该每次创建新的 String. 2. 使用 static final 变量. 3. It's reas ...