文章目录

  • Guava-retrying
    • 1. 主要相关类
      • 1.1 Attemp 类
      • 1.2 Retryer 类
      • 1.3 RetryListener
    • 2. WaitStrategies 重试等待策略
      • 2.1 ExponentialWaitStrategy 指数等待策略(WaitStrategies.exponentialWait)
      • 2.2 FibonacciWaitStrategy 斐波那契等待策略(WaitStrategies.fibonacciWait)
      • 2.3 FixedWaitStrategy 固定时长等待策略(WaitStrategies.fixedWait)
      • 2.4 RandomWaitStrategy 随机时长等待策略(WaitStrategies.randomWait)
      • 2.5 IncrementingWaitStrategy 递增等待策略(WaitStrategies.incrementingWait)
      • 2.6 ExceptionWaitStrategy 异常等待策略(WaitStrategies.exceptionWait)
      • 2.7 CompositeWaitStrategy 复合等待策略 【***】(WaitStrategies.join)
    • 3. StopStrategies 重试停止策略
      • 3.1 NeverStopStrategy 无限制重试(StopStrategies.neverStop)
      • 3.2 StopAfterAttemptStrategy 重试指定次数停止(StopStrategies.stopAfterAttempt)
      • 3.3 StopAfterDelayStrategy 重试指定时长后结束(StopStrategies.stopAfterDelay)
    • 4 AttemptTimeLimiters 任务执行时长限制(withAttemptTimeLimiter)
        • 4.1 FixedAttemptTimeLimit 指定执行时长限制(AttemptTimeLimiters.fixedTimeLimit)
      • 4.2 NoAttemptTimeLimit 无时长限制(AttemptTimeLimiters.noTimeLimit)
    • 5. BlockStrategies 阻塞策略
    • 6. 封装使用

Guava-retrying

当调用外部服务或相关接口的场景时,外部服务对调用来说一般都是不可靠的,特别现在的微服务,在网络环境较差的情况下,网络抖动非常容易导致请求超时等异常情况,这种情况就需要进行失败重试来重新调用API容错。重试策略在服务治理方面有很广泛的使用,一般通过定时检测查看服务是否存活。

Guava Retrying 是Google Guava库的一个扩展包,可以为任意函数调用创建可配置的重试机,是一个灵活方便的 重试组件,包含了多种的重试策略,最重要的是扩展起来非常容易
通过 Guava-retrying 自定义重试机制,解决系统中的各种不稳定因素,同时监控每次重试的结果和行为

gitHub地址: https://github.com/rholder/guava-retrying

1. 主要相关类

1.1 Attemp 类

Attemp ** 是一次任务重试(call),也是一次请求的结果**,主要保存当前请求的重试次数是否包含异常请求返回值

一般配合监听器使用,用于处理重试过程的具体细节

Attemp 方法 描述
long getAttemptNumber() 当前重试的次数(第几次重试)从 1 开始
long getDelaySinceFirstAttempt() 距离第一次重试延迟时间,也就是与第一次重试的时间差,单位毫秒
boolean hasException() 判断是否存在异常(可以根据异常重试/特殊结果值重试)
boolean hasResult() 判断是否返回数据结果(对满足特殊结果值进行重试)
Throwable getExceptionCause() throws IllegalStateException 异常重试的数据,获取异常信息
V getResult() throws IllegalStateException 获取重试结果信息
V get() throws ExecutionException 类似于 getResult() 返回重试结果,但是处理异常方式不同

1.2 Retryer 类

Retryer核心类,用于执行重试策略,一帮通过 RetryerBuilder 类进行构造(Factory 创建者),且 RetryerBuilder 负责将设置好的重试策咯添加到 Retryer 中,最终通过执行 Retryer 的核心方法 call 来执行重试策略

大概流程如下流程:

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

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);}}}}

RetryerBuilder 是一个 Factory 创建者,可以自定义设置重试源且支持多个重试源,可以 Exception 异常对象 和 自定义断言对象 ,通过 retryIfExceptionretryIfResult 设置,同时支持多个且能兼容。

RetryerBuilder 属性 描述
retryIfExceptionOfType(Class<? extends Throwable> exceptionClass) 只在发生特定异常的时候才重试,比如 NullPointerException :retryIfExceptionOfType(Exception.class);
通过 Predicate 实现
retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class)
Predicates.instanceOf(NullPointerException.class)))
retryIfException 抛出 runtime 异常、checked 异常时会重试,但抛出 error 不会重试
retryIfRuntimeException runtime 异常的时重试,checked 异常和 error 都不重试
retryIfExceptionOfType(Error.class) 只在抛出error重试
retryIfResult 可以指定 Callable 方法在返回值的时候进行重试
retryIfResult(Predicates.equalTo(false)) 返回 false 重试
retryIfResult(Predicates.containsPattern("_customInfo$")) _customInfo 结尾才重试

1.3 RetryListener

监听器,当执行 call 方法时,会调用监听器 RetryListener 中的 onRetry方法,通过实现然后重写该类,来实现自定义的重试逻辑机制。

 @Betapublic interface RetryListener {// 监听方法<V> void onRetry(Attempt<V> var1);}

2. WaitStrategies 重试等待策略

当执行失败后,通过 WaitStrategies 来指定不同的等待策略来进行第 n 次的重试

通过 withWaitStrategy 方法可以设置不同的重试等待策略,常见策略如下几种:

2.1 ExponentialWaitStrategy 指数等待策略(WaitStrategies.exponentialWait)

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

   @Overridepublic 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;}
// 默认倍数(乘) multiplier = 1,最大值是 Long.MAX_VALUE
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.exponentialWait());
// 指定乘数multiplier 和 最大值,第一次失败后,依次等待时长:2^1 * 100、2^2 * 100、2^3 * 100...直到最多5分钟。
// 5分钟后,每隔5分钟重试一次
// 3个参数, multiplier: 乘数, maximumTime: 最大等待时长, maximumTimeUnit: 最大等待时长单位
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES));

2.2 FibonacciWaitStrategy 斐波那契等待策略(WaitStrategies.fibonacciWait)

失败后按斐波那契数列进行等待

// 默认乘数multiplier是1,最大值是Long.MAX_VALUE
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.fibonacciWait());
// 指定乘数multiplier 和 最大值,第一次失败后,依次等待时长,1*100、1*100、2*100、3*100、5*100...直到最多5分钟,5分钟后每隔5分钟重试一次
// 3个参数, multiplier: 乘数, maximumTime: 最大等待时长, maximumTimeUnit: 最大等待时长单位
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.fibonacciWait(100, 5, TimeUnit.MINUTES));

2.3 FixedWaitStrategy 固定时长等待策略(WaitStrategies.fixedWait)

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

// 每 100 ms 重试一次
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.fixedWait(100, TimeUnit.MILLISECONDS));

2.4 RandomWaitStrategy 随机时长等待策略(WaitStrategies.randomWait)

通过设置随机等待的时长区间,或者随机等待的最大时长,从中取随机数,随机时间重试

// 最大随机时长10s
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.randomWait(10, TimeUnit.SECONDS));// 随机区间配置,[2, 10] 2-10s随机等待,四个参数分别为:
// minimumTime: 最小值,minimumTimeUnit: 最小值单位; maximumTime: 最大值, maximumTimeUnit: 最大值单位
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.randomWait(2, TimeUnit.SECONDS, 10, TimeUnit.SECONDS));

2.5 IncrementingWaitStrategy 递增等待策略(WaitStrategies.incrementingWait)

根据初始值和递增值,等待时长依次递增

// 递增配置,初始2s,后面每次在前面的基础上加3s,等待时长: 2、5、8、11、14、17、20
// 四个参数 >>> initialSleepTime: 初始等待时长,initialSleepTimeUnit: 初始等待时长单位, increment: 递增时长值, incrementTimeUnit: 递增时长单位
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.incrementingWait(2, TimeUnit.SECONDS, 3, TimeUnit.SECONDS))

2.6 ExceptionWaitStrategy 异常等待策略(WaitStrategies.exceptionWait)

根据配置异常信息指定重试的等待时长,如果异常不匹配,则等待时长为0

// 当出现空指针异常时,等待1s,出现数组越界异常时等待2s (可以配置多个)
// 参数: exceptionClass: 异常类,Function<T, Long> function: 处理函数,出现对应异常,返回等待时长RetryerBuilder<Object> builder = RetryerBuilder.newBuilder();
builder.withWaitStrategy(WaitStrategies.exceptionWait(NullPointerException.class, e -> 1000L));        builder.withWaitStrategy(WaitStrategies.exceptionWait(ArrayIndexOutOfBoundsException.class, e -> 2000L));

2.7 CompositeWaitStrategy 复合等待策略 【***】(WaitStrategies.join)

当同时满足一个或多个等待策略,等待时间为所有等待策略时间的总和。例如:

// 固定时长策略 + 异常等待策略,对于空指针异常,等待3s,其它情况等待2s
// join 组合多个策略
RetryerBuilder.newBuilder().withWaitStrategy(WaitStrategies.join(WaitStrategies.exceptionWait(NullPointerException.class, e -> 1000L),WaitStrategies.fixedWait(2, TimeUnit.SECONDS)));

3. StopStrategies 重试停止策略

指定重试多少次后停止重试,最好都配置,不然可能出现无限制的重试,通过 withStopStrategy 方法可以设置重试停止策略

3.1 NeverStopStrategy 无限制重试(StopStrategies.neverStop)

// 无限次数重试,谨慎使用
RetryerBuilder.newBuilder().withStopStrategy(StopStrategies.neverStop());

3.2 StopAfterAttemptStrategy 重试指定次数停止(StopStrategies.stopAfterAttempt)

// 重试五次结束
RetryerBuilder.newBuilder().withStopStrategy(StopStrategies.stopAfterAttempt(5));

3.3 StopAfterDelayStrategy 重试指定时长后结束(StopStrategies.stopAfterDelay)

// 10s重试,超过10s结束
RetryerBuilder.newBuilder().withStopStrategy(StopStrategies.stopAfterDelay(10, TimeUnit.SECONDS));

4 AttemptTimeLimiters 任务执行时长限制(withAttemptTimeLimiter)

表示单次任务执行时间限制【如果出现单次任务执行超时,则终止执行当前重试任务】,通过 withAttemptTimeLimiter 方法设置任务的执行时间限制

4.1 FixedAttemptTimeLimit 指定执行时长限制(AttemptTimeLimiters.fixedTimeLimit)

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

// 重试方法超过2s中断
RetryerBuilder.newBuilder().withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS));
RetryerBuilder.newBuilder().withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS), Executors.newCachedThreadPool());

4.2 NoAttemptTimeLimit 无时长限制(AttemptTimeLimiters.noTimeLimit)

RetryerBuilder.newBuilder().withAttemptTimeLimiter(AttemptTimeLimiters.noTimeLimit())

5. BlockStrategies 阻塞策略

重试等待的过程中,根据等待策略计算的时间,来阻塞执行
默认只提供一种阻塞策略:ThreadSleepStrategy,实现方式是通过 Thread.sleep() 睡眠方式来现【睡眠方式实现优势: 可以响应外部中断请求】

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

public class GuavaBlockStrategy implements BlockStrategy {@Overridepublic void block(long sleepTime) throws InterruptedException {long start = System.currentTimeMillis();long end = start;while (end - start <= sleepTime) {end = System.currentTimeMillis();}LogUtil.info("block end", start, end, sleepTime);}
}

使用:

// 自定义阻塞策略:自旋锁实现
RetryerBuilder.newBuilder().withBlockStrategy(new SpinBlockStrategy());

6. 封装使用

实际项目使用参考: Guava retry 封装使用

Guava-retrying 重试机制相关推荐

  1. 重试利器之Guava Retrying (一、介绍及简单实现)

    何为Guava Retrying? guava-retrying是基于谷歌的核心类库guava的重试机制实现,可以说是一个重试利器. Guava Retrying的使用场景? 在高并发开发的过程中,调 ...

  2. foxmail邮件加载失败重试_java retry(重试) spring retry, guava retrying 详解

    系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理:实现一个按条件,查询用户信息的服务. 小明:好的.没问题. 代码 UserServi ...

  3. java retry(重试) spring retry, guava retrying 详解

    转载 自 http://blog.51cto.com/9250070/2156431 系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理 ...

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

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

  5. java retry_Retry重试机制

    对于重试机制有两个一个是Guava,另一个是spring的. Guava retryer工具 pom引用 com.github.rholder guava-retrying 2.0.0 测试方法 Re ...

  6. 使用 guava-retrying 实现灵活的重试机制

    我们的后端业务系统可能会出现接口调用失败.网络拥塞超时.任务执行失败.系统错误等异常情况,需要进行重试操作.但某些场景下我们对重试有特殊要求,比如延迟重试.降频重试等,此时自己编写重试代码会很繁琐,在 ...

  7. 【转载】Java重试机制

    重试机制在分布式系统中,或者调用外部接口中,都是十分重要的. 重试机制可以保护系统减少因网络波动.依赖服务短暂性不可用带来的影响,让系统能更稳定的运行的一种保护机制. 为了方便说明,先假设我们想要进行 ...

  8. 11. kafka重试机制解读

    前面对kafka的学习中已经了解到KafkaProducer通过设定参数retries,如果发送消息到broker时抛出异常,且是允许重试的异常,那么就会最大重试retries参数指定的次数. 本片文 ...

  9. java中的失败重试机制总结

    应用中需要实现一个功能: 需要将数据上传到远程存储服务,同时在返回处理成功情况下做其他操作.这个功能不复杂,分为两个步骤:第一步调用远程的Rest服务上传数据后对返回的结果进行处理:第二步拿到第一步结 ...

最新文章

  1. WordCount运行详解
  2. 如何在Hadoop上运行TensorFlow【部署】
  3. ITK:将自定义操作应用于图像中的每个像素
  4. oracle中的merge into用法解析
  5. javascript基础修炼(11)——DOM-DIFF的实现
  6. 2018年全国多校算法寒假训练营练习比赛(第一场)G 圆圈
  7. windows rt运行android,Move from Android to WinRT
  8. 《神经网络与深度学习》课程笔记(2)-- 神经网络基础之逻辑回归
  9. 定时器(setTimeout/setInterval)调用带参函数失效解决方法
  10. 31省“5G应用”主攻方向+责任单位一览!
  11. 开关电源(DC/DC)和线性电源(LDO低压差线性稳压器)的区别
  12. shopex网店系统数据库安装失败解决方法
  13. 狄拉克量子力学原理【1】态叠加原理
  14. Linux系统下卸载VMware Workstation软件
  15. 七夕表白代码合集,建议收藏!!!
  16. LeetCode 1114:按序打印
  17. Android手机teams,在iOS和Android上自定义Microsoft Teams体验的三种最佳方法
  18. 20230106 作业
  19. QQ也出网页版的了- WebQQ公测中
  20. Faster RCNN总结(优缺点说明)

热门文章

  1. Themida是否支持PowerBuilder应用程序的保护?—— Themida常见问答集锦
  2. 靶场复现————平行越权、垂直越权
  3. java基础入门txt下载地址_java基础入门-ZipOutputStream打包下载
  4. linux运行QQ,微信,企业微信 (非deepin)
  5. 四步教你破解隔壁老王的Wi-Fi密码,蹭网没商量!
  6. 5月份必火20条爆笑段子
  7. 订单交期迟滞,销售回应慢,怎么解决客户问题?
  8. win10用hdmi线与电视连接了,但不显示的原因
  9. 退休手续如何办理 具体流程是什么?
  10. 【TB-02模组专题⑤】微信小程序通讯TB02 模块控制 STM32 单片机LED灯