
使用 Guava-Retry 优雅的实现重处理
大家好,我是陈哥~
在日常开发中,尤其是在微服务盛行的时代下,我们在调用外部接口时,经常会因为第三方接口超时、限流等问题从而造成接口调用失败,那么此时我们通常会对接口进行重试,那么问题来了,如何重试呢?该重试几次呢?如果要设置重试时间超过多长时间后还不成功就不重试了该怎么做呢?所幸guava-retrying为我们提供了强大而简单易用的重试框架guava-retrying。
“
guava-retrying是谷歌的Guava库的一个小扩展,允许为任意函数调用创建可配置的重试策略,比如与正常运行时间不稳定的远程服务对话的函数调用。
”
在前面陈哥也介绍过一种重试框架Spring-Retry:Spring Boot 优雅的实现重处理功能
1. pom依赖
2. 使用示例
我们可以通过RetryerBuilder来构造一个重试器,通过RetryerBuilder可以设置什么时候需要重试(即重试时机)、停止重试策略、失败等待时间间隔策略、任务执行时长限制策略
先看一个简单的例子:
输出:
3. 重试时机
RetryerBuilder的retryIfXXX()方法用来设置**在什么情况下进行重试,总体上可以分为根据执行异常进行重试和根据方法执行结果进行重试两类。关注公众号:“码猿技术专栏”,回复关键词:“1111” 获取阿里内部Java调优手册
3.1 根据异常进行重试
方法 | 描述 |
retryIfException() | 当方法执行抛出异常 isAssignableFrom Exception.class 时重试 |
retryIfRuntimeException() | 当方法执行抛出异常 isAssignableFrom RuntimeException.class 时重试 |
retryIfException(Predicate exceptionPredicate) | 这里当发生异常时,会将异常传递给exceptionPredicate,那我们就可以通过传入的异常进行更加自定义的方式来决定什么时候进行重试 |
retryIfExceptionOfType(Class<? extends Throwable> exceptionClass) | 当方法执行抛出异常 isAssignableFrom 传入的exceptionClass 时重试 |
3.2 根据返回结果进行重试
retryIfResult(@Nonnull Predicate resultPredicate) 这个比较简单,当我们传入的resultPredicate返回true时则进行重试
4. 停止重试策略StopStrategy
停止重试策略用来决定什么时候不进行重试,其接口com.github.rholder.retry.StopStrategy,停止重试策略的实现类均在com.github.rholder.retry.StopStrategies中,它是一个策略工厂类。
4.1 NeverStopStrategy
此策略将永远重试,永不停止,查看其实现类,直接返回了false
4.2 StopAfterAttemptStrategy
当执行次数到达指定次数之后停止重试,查看其实现类:
4.3 StopAfterDelayStrategy
当距离方法的第一次执行超出了指定的delay时间时停止,也就是说一直进行重试,当进行下一次重试的时候会判断从第一次执行到现在的所消耗的时间是否超过了这里指定的delay时间,查看其实现:
5. 重试间隔策略、重试阻塞策略
这两个策略放在一起说,它们合起来的作用就是用来控制重试任务之间的间隔时间,以及如何任务在等待时间间隔时如何阻塞。也就是说WaitStrategy决定了重试任务等待多久后进行下一次任务的执行,BlockStrategy用来决定任务如何等待。它们两的策略工厂分别为com.github.rholder.retry.WaitStrategies和BlockStrategies。关注公众号:“码猿技术专栏”,回复关键词:“1111” 获取阿里内部Java调优手册
5.1 BlockStrategy
5.1.1 ThreadSleepStrategy
这个是BlockStrategies,决定如何阻塞任务,其主要就是通过**Thread.sleep()**来进行阻塞的,查看其实现:
5.2 WaitStrategy
5.2.1 IncrementingWaitStrategy
该策略在决定任务间隔时间时,返回的是一个递增的间隔时间,即每次任务重试间隔时间逐步递增,越来越长,查看其实现:
该策略输入一个起始间隔时间值和一个递增步长,然后每次等待的时长都递增increment时长。
5.2.2 RandomWaitStrategy
顾名思义,返回一个随机的间隔时长,我们需要传入的就是一个最小间隔和最大间隔,然后随机返回介于两者之间的一个间隔时长,其实现为:
5.2.3 FixedWaitStrategy
该策略是返回一个固定时长的重试间隔。查看其实现:
5.2.4 ExceptionWaitStrategy
该策略是由方法执行异常来决定是否重试任务之间进行间隔等待,以及间隔多久。
5.2.5 CompositeWaitStrategy
这个没啥好说的,顾名思义,就是一个策略的组合,你可以传入多个WaitStrategy,然后所有WaitStrategy返回的间隔时长相加就是最终的间隔时间。查看其实现:
5.2.6 FibonacciWaitStrategy
这个策略与IncrementingWaitStrategy有点相似,间隔时间都是随着重试次数的增加而递增的,不同的是,FibonacciWaitStrategy是按照斐波那契数列来进行计算的,使用这个策略时,我们需要传入一个乘数因子和最大间隔时长,其实现就不贴了
5.2.7 ExponentialWaitStrategy
这个与IncrementingWaitStrategy、FibonacciWaitStrategy也类似,间隔时间都是随着重试次数的增加而递增的,但是该策略的递增是呈指数级递增。查看其实现:
6. 重试监听器RetryListener
当发生重试时,将会调用RetryListener的onRetry方法,此时我们可以进行比如记录日志等额外操作。
输出:
7. 重试原理
其实到这一步之后,实现原理大概就很清楚了,就是由上述各种策略配合从而达到了非常灵活的重试机制。在这之前我们看一个上面没说的东东-Attempt
通过接口方法可以知道Attempt这个类包含了任务执行次数、任务执行异常、任务执行结果、以及首次执行任务至今的时间间隔,那么我们后续的不管重试时机、还是其他策略都是根据此值来决定。
接下来看关键执行入口Retryer##call:
8. 总结
通篇下来可以看到其实核心实现并不难,但是此框架通过建造者模式和策略模式组合运用,提供了十分清晰明了且灵活的重试机制,其设计思路还是值得借鉴学习!
文章转载自公众号:码猿技术专栏
