guava-retrying是Google Guava库的一个扩展包,可以为任意函数调用创建可配置的重试机制。该扩展包比较简单,大约包含了10个方法和类,官网:GitHub - rholder/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.https://github.com/rholder/guava-retrying

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

看一个使用例子:

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder().retryIfException().retryIfResult(aBoolean -> Objects.equals(aBoolean, false)).withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(10, TimeUnit.SECONDS, Executors.newCachedThreadPool())).withWaitStrategy(WaitStrategies.fixedWait(5, TimeUnit.SECONDS)).withStopStrategy(StopStrategies.stopAfterAttempt(5)).withRetryListener(new RetryListener() {@Overridepublic <V> void onRetry(Attempt<V> attempt) {System.out.print("retry time=" + attempt.getAttemptNumber());}}).build();
try {retryer.call(() -> {// 逻辑处理return null;});
} catch (Exception e) {System.out.println("exception:" + e);
}

1、常用类介绍

1)Attemp

Attemp既是一次任务重试(call),也是一次请求的结果,记录了当前请求的重试次数,是否包含异常和请求的返回值。我们可以配合监听器使用,用于记录重试过程的细节,常用的方法有如下几个:

  • getAttemptNumber(),表示准备开始第几次重试;
  • getDelaySinceFirstAttempt(),表示距离第一次重试的延迟,也就是与第一次重试的时间差,单位毫秒;
  • hasException(),表示是异常导致的重试还是正常返回;
  • hasResult(),表示是否返回了结果;因为有时候是因为返回了特定结果才进行重试;
  • getExceptionCause(),如果是异常导致的重试,那么获取具体具体的异常类型;
  • getResult(),返回重试的结果;
  • get(),如果有的话,返回重试的结果;和getResult不同的在于对异常的处理;

2)Retryer:

Retryer是最核心的类,是用于执行重试策略的类,通过RetryerBuilder类进行构造,并且RetryerBuilder负责将设置好的重试策咯添加到Retryer中,最终通过执行Retryer的核心方法call来执行重试策略(一次任务的执行是如何进行的?),call方法源码如下:

public V call(Callable<V> callable) throws ExecutionException, RetryException {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);}}}
}

大致流程如下:

  1. 判断是否超过任务时长限制;
  2. 执行重试listener
  3. 判断是否满足重试条件
  4. 重试停止策略;
  5. 重试等待策略;
  6. 组册策略;

3)RetryListener:

当执行call方法时,会调用监听器中的onRetry方法。重写该类的onRetry方法可以实现自定义逻辑,例如:

@Slf4j
public class RetryLogListener implements RetryListener {@Overridepublic <V> void onRetry(Attempt<V> attempt) {// 第几次重试,(注意:第一次重试其实是第一次调用)log.info("retry time : [{}]", attempt.getAttemptNumber());// 距离第一次重试的延迟log.info("retry delay : [{}]", attempt.getDelaySinceFirstAttempt());// 重试结果: 是异常终止, 还是正常返回log.info("hasException={}", attempt.hasException());log.info("hasResult={}", attempt.hasResult());// 是什么原因导致异常if (attempt.hasException()) {log.info("causeBy={}" , attempt.getExceptionCause().toString());} else {// 正常返回时的结果log.info("result={}" , attempt.getResult());}log.info("log listen over.");}
}

2、WaitStrategies 重试等待策略

当执行失败后,用来指定不同的等待策略来进行第二、三...次的重试。

通过withWaitStrategy方法可以设置不同的重试等待策略,WaitStrategies.java中定义了很多重试等待方法,常见的有:

1)ExponentialWaitStrategy 指数等待策略

实现了指数补偿算法(Exponential Backoff),其源码根据重试次数计算等待时长,如下:

@Override
public long computeSleepTime(Attempt failedAttempt) {double exp = Math.pow(2, failedAttempt.getAttemptNumber());long result = Math.round(multiplier * exp);if (result > maximumWait) {result = maximumWait;}return result >= 0L ? result : 0L;
}

例如:

WaitStrategies.exponentialWait() . #默认乘数multiplier是1,最大值是Long.MAX_VALUE

也可以指定乘数和最大值:

.withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES))

第一次失败后,依次等待时长:2^1 * 100;2^2 * 100;2^3 * 100;...直到最多5分钟。 5分钟后,每隔5分钟重试一次。

2)FibonacciWaitStrategy 斐波那契等待策略

失败后,按照斐波那契数列进行等待,例如:

WaitStrategies.fibonacciWait() #默认乘数multiplier是1,最大值是Long.MAX_VALUE

也可以指定乘数和最大值

.withWaitStrategy(WaitStrategies.fibonacciWait(100, 2, TimeUnit.MINUTES))

第一次失败后,依次等待时长:1*100;1*100;2*100;3*100;5*100;...直到最多2分钟,2分钟后每隔2分钟重试一次;

3)FixedWaitStrategy 固定时长等待策略

失败后,将等待固定的时长进行重试,例如:

withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS))

4)RandomWaitStrategy 随机时长等待策略

可以设置一个随机等待的最大时长,也可以设置一个随机等待的时长区间,例如:

withWaitStrategy(WaitStrategies.randomWait(10, TimeUnit.SECONDS));

withWaitStrategy(WaitStrategies.randomWait(1, TimeUnit.SECONDS, 10, TimeUnit.SECONDS));

5)IncrementingWaitStrategy 递增等待策略

根据初始值和递增值,等待时长依次递增。例如:

withWaitStrategy(WaitStrategies.incrementingWait(1, TimeUnit.SECONDS, 5, TimeUnit.SECONDS))

第一次失败后,将依次等待1s;6s(1+5);11(1+5+5)s;16(1+5+5+5)s;...

6)ExceptionWaitStrategy 异常等待策略

根据所发生的异常指定重试的等待时长;如果异常不匹配,则等待时长为0;

withWaitStrategy(WaitStrategies.exceptionWait(ArithmeticException.class, e -> 1000L))

7)CompositeWaitStrategy 复合等待策略

如果所执行的程序满足一个或多个等待策略,那么等待时间为所有等待策略时间的总和。例如:

.withWaitStrategy(WaitStrategies.join(WaitStrategies.exceptionWait(ArithmeticException.class, e -> 1000L),WaitStrategies.fixedWait(5, TimeUnit.SECONDS)))

3、BlockStrategies 阻塞策略

在重试等待过程中,根据等待策略计算的时间,来阻塞。默认只提供一种阻塞策略:ThreadSleepStrategy,实现方式是通过Thread.sleep(sleepTime)来实现

默认的阻塞策略是线程休眠,可以自定义阻塞策略,这里使用自旋锁实现,不阻塞线程。

package net.ijiangtao.tech.framework.spring.ispringboot.demo.retryer.guava.strategy;import com.github.rholder.retry.BlockStrategy;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;import java.time.Duration;
import java.time.LocalDateTime;/*** 自旋锁的实现, 不响应线程中断*/
@Slf4j
@NoArgsConstructor
public class SpinBlockStrategy implements BlockStrategy {@Overridepublic void block(long sleepTime) throws InterruptedException {LocalDateTime startTime = LocalDateTime.now();long start = System.currentTimeMillis();long end = start;log.info("[SpinBlockStrategy]...begin wait.");while (end - start <= sleepTime) {end = System.currentTimeMillis();}//使用Java8新增的Duration计算时间间隔Duration duration = Duration.between(startTime, LocalDateTime.now());log.info("[SpinBlockStrategy]...end wait.duration={}", duration.toMillis());}
}

使用时:

//自定义阻塞策略:自旋锁
.withBlockStrategy(new SpinBlockStrategy())

4、StopStrategies 重试停止策略

用来指定重试多少次后停止重试。因为无限制的重试不一定是一个好的方式,可能会给下游系统带来灾难性。

通过withStopStrategy方法可以设置重试停止策略,StopStrategies.java中定义了很多重试停止策略,常见的有:

1)NeverStopStrategy

无限重试,例如:

withStopStrategy(StopStrategies.neverStop())

2)StopAfterAttemptStrategy

重试n次后,停止;例如:

withStopStrategy(StopStrategies.stopAfterAttempt(3))

3)StopAfterDelayStrategy

重试多长时间后,停止;例如:

withStopStrategy(StopStrategies.stopAfterDelay(3, TimeUnit.MINUTES))

5、AttemptTimeLimiters 任务执行时长限制

这个表示单次任务执行时间限制(如果单次任务执行超时,则终止执行当前任务);

通过withAttemptTimeLimiter方法可以设置任务的执行时间限制,常见的有:

1)NoAttemptTimeLimit 无时长限制

顾名思义,不限制执行时长;每次都是等执行任务执行完成之后,才进行后续的重试策咯。

.withAttemptTimeLimiter(AttemptTimeLimiters.noTimeLimit())

2) FixedAttemptTimeLimit

可以指定任务的执行时长限制,并且为了控制线程管理,最好指定相应的线程池。

.withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(10, TimeUnit.SECONDS));

.withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(10, TimeUnit.SECONDS, Executors.newCachedThreadPool()));

参考:

https://www.jianshu.com/p/a289dde63043

https://juejin.cn/post/6844903785031008263

guava-retry介绍相关推荐

  1. Google Guava Retry 优雅的重试方案

    Google Guava Retry 优雅的重试方案 前言 使用场景 什么场景不适合重试 了解幂等性 一.Guava Retry是什么? 与Spring retry比较 二.使用步骤 1.引入库 2. ...

  2. [由零开始]Guava Cache介绍和用法

    Guava Cache介绍 Guava是Google提供的一套Java工具包,而Guava Cache是一套非常完善的本地缓存机制(JVM缓 存). Guava cache的设计来源于CurrentH ...

  3. Guava Cache介绍

    1.缓存回顾: 使用场景:互联网,数据越来越多,用户越来越多,并发量.吞吐量越来越大 使用数据库存储,分库分表,也不能满足要求,使用缓存,减轻数据库的压力 临时存储的数据 其他的场景:Session分 ...

  4. 谷歌Guava LoadingCache介绍

      在工作中,加Cache是非常常见的一种性能优化手段,操作系统底层.计算机硬件层为了性能优化加了各种各样的Cache,当然大多数都是对应用层透明的.但如果你想在应用层加Cache的话,可能就需要你自 ...

  5. guava retry使用

    git地址:https://github.com/rholder/guava-retrying maven依赖: <dependency><groupId>com.google ...

  6. Guava的介绍与使用示例

    Guava是什么 Guava,翻译是番石榴.这里指的是Google Core Libraries for Java, 即Google开发的Java核心库的扩展. 其提供新的集合类型(比如multima ...

  7. Google Guava新手教程

    以下资料整理自网络 一.Google Guava入门介绍 引言 Guavaproject包括了若干被Google的 Java项目广泛依赖 的核心库,比如:集合 [collections] .缓存 [c ...

  8. shell 执行失败重试_Smart Retry主要是用来进行方法重试

    Smart Retry Smart Retry主要是用来进行方法重试的.和Guava Retry.Spring Retry相比,Smart Retry最大的特点是异步重试,支持持久化,系统重启之后可以 ...

  9. Guava collections -- Table

    Guava全文介绍地址:Google Guava 这次主要介绍是的com.google.common.collect.Table.Table是包含相有序的一对key值,称为行键和一个列键,一个值的集合 ...

  10. Google guava 事件总线 EventBus 进程内消息队列

    Google guava 事件总线 EventBus 创建事件总线流程 码代码 引入依赖 一个简单的事件处理 监听者 创建事件生产者总线.注册事件监听者.发送事件 运行结果 扩展 多个事件监听者加De ...

最新文章

  1. 给大家介绍一位中科院师兄,读研时通过实习和比赛收入五十万
  2. Slide:深入了解Oracle自动内存管理ASMM by Maclean Liu
  3. Android开发学习笔记:对话框浅析
  4. linux printf 刷新,linux下printf中\n刷新缓冲区的疑问(已解决)
  5. python怎么将输入的数字变成列表_Python键盘输入转换为列表的实例
  6. 关于Android SDK工具Lint的误报分析
  7. shell中的>/dev/null 2>1(转载)
  8. .NET设计模式(3): 抽象工厂模式
  9. JPA学习笔记---JPA实体Bean的建立+配置文件+junit测试+自动生成(对应实体Bean的)数据库表+插入数据
  10. ECharts 3.0 初学感想及学习中遇到的瓶颈
  11. [Diary]花草本没有错
  12. python正则表达式面试题,带有utf8问题的python正则表达式
  13. 学习笔记——激励函数activation func、损失函数loss func
  14. 数学 三角函数 sin 正弦、cos 余弦、tan 正切、cot 余切、sec 正割、csc 余割 简介
  15. Yolov5训练自己的数据集+TensorRT加速+Qt部署
  16. T4M插件放入unity后怎么找不到_Unity动画系统详解4:如何用代码控制动画?
  17. HTML 表格合并(表格合并行属性 rowspan 将多行合并成一行)
  18. windows 2008 R2 感染Ramnit.x病毒查杀方法
  19. 镜头离焦对于ToF深度的影响分析
  20. net view 时报错 发生系统错误 6118 解决

热门文章

  1. 在 Nebula K8s 集群中使用 nebula-spark-connector 和 nebula-algorithm
  2. leetcode算法 森林中的兔子
  3. 微信小程序扫码功能的使用
  4. win7电脑插音响没声音的解决教程--win10专业版
  5. 腾讯云如何设置二级域名?
  6. 梦殇 chapter six
  7. 拳王公社:虚拟资源项目赚钱方法?前2种最常见,第3种鲜为人知
  8. Excel VBA UserForm窗体置顶
  9. 叶片静频测量方法理论基础(自振法上)
  10. matlab 仿真步长,[转载]matlab 仿真步长设置