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