guava-retrying重试机制

简介

在项目中, 我们一定会遇到在某一步骤会重试多次, 直到成功, 比如: 爬虫在爬取网页的时候,调用第三方接口的时候。

在必要的地方加上错误重试是很有必要的, 多次重试可以提高程序的健壮性, 减少业务的错误。

一般来说, 我们可能就简单的使用for,while循环多次,直到达到某个条件退出, 还是写的比较简单的。

现在我们就简绍guava-retry, 是由google研发的一款重试工具。

使用

pom.xml

<dependencies>
    <!-- https://mvnrepository.com/artifact/com.github.rholder/guava-retrying -->
    <dependency>
        <groupId>com.github.rholder</groupId>
        <artifactId>guava-retrying</artifactId>
        <version>2.0.0</version>
    </dependency>
</dependencies>

GuavaRetry.java

package top.itkaito.java.tool.t01;

import com.github.rholder.retry.*;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
* guava-retry
*/
public class GuavaRetry {
public static void main(String[] args) {
//            try{
//                sendMsg("hello tom!!!");
//            }catch (Throwable t){
//                t.printStackTrace();
//            }

        Retryer<Object> retry = RetryerBuilder.newBuilder()
                // 抛异常
                .retryIfException()
                // 停止策略
                .withStopStrategy(StopStrategies.neverStop())
                // 重试等待策略
                .withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))
                // 重试运行时间限制
                .withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(3, TimeUnit.SECONDS))
                // 监听
                .withRetryListener(new RetryListener() {
                    @Override
                    public <V> void onRetry(Attempt<V> attempt) {
                        long attemptNumber = attempt.getAttemptNumber();
                        System.out.println(String.format("第%s次重试", attemptNumber));
                    }
                }).build();
    
        try {
            Object call = retry.call(() -> {
                sendMsg("hello world!!!");
                return true;
            });
            System.out.println(call);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (RetryException e) {
            e.printStackTrace();
        }
    
    
    }

    public static void sendMsg(String msg) {
        // 模拟一定几率出错
        int i = new Random().nextInt(10);
        if (i <= 4) {
            throw new RuntimeException("未知情况, 程序出错");
        }
        
        if (i <= 7) {
            try {
                Thread.sleep(10*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(String.format("your msg:%s has send success", msg));
    }

}

运行我们发现程序会报错, 详情见下面参考 1

解决方案: 将guava依赖改为19.0版本

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>19.0</version>
</dependency>

讲解

RetryBuilder

RetryBuilder用了创建者模式, 用来创建Retryer的。

StopStrategy

停止策略, 在程序运行出现异常后, 是否停止

停止策略
StopStrategies.neverStop() 永不停止
StopStrategies.stopAfterAttempt(attemptNumber) 重试attemptNumber次后停止
StopStrategies.stopAfterDelay(duration, timeUnit) 延迟指定时间后停止

WaitStrategy

等待策略, 在程序运行出现异常后, 等下下次重试的策略

停止策略
WaitStrategies.noWait() 不停止, 立即下次重试
fixedWait(sleepTime, timeUnit) 等待指定时间, 再进行下次重试
randomWait(..) 等待随机时间, 再进行下次重试
incrementingWait(...) 每次重试后, 等待的时间会变长
exponentialWait(..) 等待时间指数级变长??
fibonacciWait(...) 斐波那契变长???

AttemptTimeLimiter

重试时间限制, 单词重试允许的时间

重试时间策略
AttemptTimeLimiters.noTimeLimit() 没时间限制
fixedTimeLimit(...) 固定时间限制

RetryListener

重试监听器, 每次重试的时候, 会回调该类的onRetry方法

参考

  1. [WebDriver]解决报错:tried to access method com.google.common.util.concurrent.SimpleTimeLimiter