前言
小伙伴是不是经常遇到接口调用异常的情况?
很多小伙伴的实现方式是写个循环调用;
- "color:#444444">"background-color:#f6f6f6">"color:#333333">for("color:#397300">int i= "color:#880000">1 ;i<= "color:#880000">3 ;i++){
- "color:#333333">try {
- "color:#333333">if (doExec()){
- "color:#333333">break ;
- }
- }"color:#333333">抓{
- }
- }
方式是比较简单的,但是非常不灵活,今天出门不能很顾虑,但是这种情况很顾虑。实现给大家带来一个重试重试的组件,流行度很明亮,即是番石榴重试组件。功能强大,是平日旅行的必备工具。
引用
- < dependency >
- < groupId > com.github.rholder groupId >
- < artifactId > guava- retrying< / artifactId >
- < version > 2.0.0 version >
-
- -->
-
-
-
-
-
-
- -->
- 依赖>
guava-retrying包中应用有相关的guava版本依赖,如果和自身项目突破可以解决。
示例
执行方法
- @Service public class RetryService {
- private static Logger logger = LoggerFactory.getLogger ( RetryService.class )
- ; _
- 私有AtomicInteger计数= 新AtomicInteger ( 0 );
- 公共int doExec(){
- logger.info( "调用了{}次" , count .incrementAndGet());
- if ( count . get () % 2 == 0 ){
- throw new Exception ( "----->异常了哦" );
- }
- 返回 计数。得到();
- }
- }
其中定义了doExec方法,每次调用计数加1,如果是的倍数就抛。
调用方法
- 公共字符串 test01(){
- Retryer
retryer = RetryerBuilder.newBuilder() - .retryIfRuntimeException()
- //retryIfResult 表达式返回true,则重试
- .retryIfResult(结果 -> {
- 如果(结果 % 3 == 0){
- logger.info( "----->应该试重了" );
- 返回 真;
- }
- 返回 假;
- })
- .withStopStrategy(StopStrategies.stopAfterAttempt( 3 ))
- 。建造();
- 试试{
- retryer.call(() -> retryService.doExec());
- }捕捉(ExecutionException e){
- logger.error( "异常1:{}" ,e.getMessage());
- }捕捉(重试异常 e){
- logger.error( "异常:{}" ,e.getMessage());
- }
- 返回 “确定”;
- }
从上面代码中,我们就可以实现条件重试。
番石榴的重试思想可分为重试重条件、停试策略、重试间歇策略
表示在什么情况下,重试。重试组件中的RetryerBuilder的retryIfXXX()方法使用设置在什么情况下进行重试,用户可以在什么情况下根据执行异常进行重试和根据方法执行结果进行重试三个。
可知异常重试
1、retryIfException() 当执行方法抛出异常时重试
2、retryIfRuntimeException()当方法执行抛出异常RuntimeException时重试
3、retryIfExceptionOfType(exceptionClass)当方法执行抛出异常具体哪个异常时重试
4、retryIfException(Predicate p)自定义异常什么情况下重试
可知返回结果重试
retryIfResult(@Nonnull Predicate
- //返回真时,重试
- .retryIfResult(结果 -> {
- 如果(结果 % 3 == 0){
- logger.info( "----->应该试重了" );
- 返回 真;
- }
- 返回 假;
- })
上面的结果代表的是返回值,判断返回值对3取余,返回真实时则进行重试。
重试需要提供停止重试的策略withStopStrategy,简单的方式就是重试次数最多
1、StopAfterAttempt策略
从面字上面就知道什么英文,即在执行次数指定次数之后停止重试。
<span style="color:#444444">#f6f6f6">#880000">.withStopStrategy ( #333333">StopStrategies #880000">.stopAfterAttempt (3))
2、永不停止策略
永远重试,一直重试
<span style="color:#444444">#f6f6f6">#880000">.withStopStrategy ( #333333">StopStrategies #880000">.neverStop ())
3、StopAfterDelay策略
设定一个最长的时间;只要设定一次执行最多执行10s,不计次数,试试的时间与第一次的时间差,最长时间,执行则执行,返回执行重试重试异常
<span style="color:#444444">#f6f6f6">#880000">.withStopStrategy ( #333333">StopStrategies #880000">.stopAfterDelay (10, #333333">TimeUnit #880000">.SECONDS ))
在重试场景中,我们最好的试试重试的间隔,如果没有间隔,可能有连续的重试失败。
等待策略
1、固定等待策略
固定时长重试间歇。
<span style="color:#444444">#f6f6f6">#880000">.withWaitStrategy ( #333333">WaitStrategies #880000">.fixedWait (1, #333333">TimeUnit #880000">.SECONDS ))
那就是重试间隔为1秒。
2、随机等待策略
随时的间隔时长
<span style="color:#444444">#f6f6f6">#880000">.withWaitStrategy ( #333333">WaitStrategies #880000">.randomWait (1, #333333">TimeUnit #880000">.SECONDS ,5, #333333">TimeUnit #880000">.SECONDS ))
第1个间隔时间长,最小个间隔时间长的时间;每次间隔时间是第二个参数。
3、增量等待策略
递增的间隔时间长,即每次任务重试的时间递增,越来越长
<span style="color:#444444">#f6f6f6">#880000">.withWaitStrategy ( #333333">WaitStrategies #880000">.incrementingWait (3, #333333">TimeUnit #880000">.SECONDS ,1, #333333">TimeUnit #880000">.SECONDS ))
该策略输入一个每隔一段时间的等待时间增加一个步长,然后每次值和时间的长都递增。
4、异常等待策略
根据不同的不同,决定不同的间隔时长。
- .withWaitStrategy (WaitStrategies.exceptionWait(Exception.class, new Function
() { - @Override
- public @Nullable Long apply( @Nullable异常输入) {
- if (input instanceof NullPointerException){
- 返回 1 * 1000升;
- }else if (input instanceof IndexOutOfBoundsException){
- 返回 2 * 1000升;
- }else if (input instanceof IllegalStateException){
- 返回 3 * 1000升;
- }
- 返回0升;
- }
- }))
这个的代码一看就知道了。
是一些常见的策略,不常用的策略,小伙伴们先等待。
我们需要记录一个非常重要的试试机会关注系统的产品。
我们来介绍一下重试监听器。
重试监听器RetryListener
当重试时,会调用RetryListener的onRetry方法,这样我们就可以做一些自定义的重试的额外任务。
定义一个类,继承RetryListener接口
- 公共 类 MyRetryListener 实现 RetryListener {
-
- 私有 静态Logger logger = LoggerFactory.getLogger(MyRetryListener.class);
-
- @Override
- public
void onRetry (Attempt 尝试) { - if (attempt.hasResult()){
- logger.info( "===> 方法返回的结果:{}" ,attempt.getResult());
- }
-
- if (attempt.hasException()){
- logger.info( "===>第{}次执行, 异常:{}" ,attempt.getAttemptNumber(),attempt.getExceptionCause()== null ? "" : attempt.getExceptionCause().getMessage());
- 返回;
- }
- logger.info( "===>第{}次执行" ,attempt.getAttemptNumber());
- }
- }
在RetryerBuilder中加入;
.withRetryListener (new MyRetryListener())
这样就实现了监听业务
重试原理
guava-retrying的组件功能还是比较强大的,我们可以看看内核的代码
- public V call (Callable
callable) throws ExecutionException, RetryException { - long startTime = System.nanoTime();
-
- // 执行次数从
- 1Startfor ( int attemptNumber = 1 ; ; attemptNumber++) {
- 尝试
尝试; - try {
- //尝试执行
- V result = attemptTimeLimiter. 调用(可调用);
-
- // 执行成功则将结果封装为ResultAttempt
- attempt = Retryer.ResultAttempt
(result, tryNumberlis(System.nanoTime() - startTime)); - } catch
- ( Thrable t) { // 执行异常则将封装结果为ExceptionAttempt
-
- }
-
- // 这里将执行结果传给RetryListener 做一些额外的事情
- for (RetryListener listener : listeners) {
- listener.onRetry(尝试);
- }
- //这个就是决定是否要进行重试的地方,如果不进行重试直接返回结果,执行成功就返回结果,执行失败就返回异常
- if (!rejectionPredicate.apply(attempt)) {
- return attempt.get() ;
- }
-
- // 到这里,说明需要重试,此时先决定是否达到异常则停止重试的时机,如果了则直接返回
- if (stopStrategy.shouldStop(attempt)) {
- throw new RetryException(attemptNumber, attempt) ;
- } else {
- // 决定重试间隔
- long sleepTime = waitStrategy.computeSleepTime(attempt);
- 尝试{
- // 进行阻尼
- blockStrategy.block(sleepTime);
- }捕捉(InterruptedException e){
- Thread.currentThread().interrupt();
- 抛出 新的重试异常(尝试编号,尝试);
- }
- }
- }
-
- }