guava-retrying基于guava的重试模块

简介:

简介

The guava-retrying module provides a general purpose method for retrying arbitrary Java code with specific stop, retry, and exception handling capabilities that are enhanced by Guava's predicate matching.

maven依赖

<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>2.0.0</version>
</dependency>

使用说明

简单的demo

Callable<Boolean> callable = new Callable<Boolean>() {
    public Boolean call() throws Exception {
        return true; // do something useful here
    }
};

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfResult(Predicates.<Boolean>isNull())
        .retryIfExceptionOfType(IOException.class)
        .retryIfRuntimeException()
        .withStopStrategy(StopStrategies.stopAfterAttempt(3))
        .build();
try {
    retryer.call(callable);
} catch (RetryException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

首先重试的内容主体必须是一个Callable的实现,重试是根据其call方法的执行状态(异常、返回值)定制的重试策略来进行重试的。
比如上面的列子。重试者retryer由建造者RetryerBuilder创建的,RetryerBuilder提供了各种方法来定制重试策略。

根据返回是否是null来决定是否重试

.retryIfResult(Predicates.<Boolean>isNull())

根据异常类型是否是IOException来决定是否重试

.retryIfExceptionOfType(IOException.class)

多个类型的时候还可以使用Predicate

Retryer<Void> retryer1 = RetryerBuilder.<Void>newBuilder()
        .retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class),
                                        Predicates.instanceOf(IllegalStateException.class)))
        .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
        .build();

根据异常是否是RuntimeException来决定是否重试

.retryIfRuntimeException()

设置重试的终止策略,尝试3次后终止

.withStopStrategy(StopStrategies.stopAfterAttempt(3))

除了上述重试判断外判断重试的方式与策略还有很多,比如

重试判断

返回值匹配

.retryIfResult(Predicates.containsPattern("_error$"))
.retryIfResult(Predicates.equalTo(2))

终止策略

常用的终止策略在com.github.rholder.retry.StopStrategies中。

尝试3次后终止

.withStopStrategy(StopStrategies.stopAfterAttempt(3))

30s后终止

.withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS))

不终止

.withStopStrategy(StopStrategies.neverStop())

等待策略

很多场景下并不是立即重试(环境影响,立即重试的失败率依旧很高),一般会等待一段时间后继续重试。
提供了等待策略,常用策略在com.github.rholder.retry.WaitStrategies中。

比如

等待固定时间

.withWaitStrategy(WaitStrategies.fixedWait(5L, TimeUnit.SECONDS))

每次等待时间递增

.withWaitStrategy(WaitStrategies.incrementingWait(3, TimeUnit.SECONDS,1,TimeUnit.SECONDS))

斐波那契式等待

.withWaitStrategy(WaitStrategies.fibonacciWait())

重试监听

guava-retrying提供每次重试的监听机制,每次重试后会回调注册的监听者,按顺序依次执行。

监听者必须实现RetryListener.onRetry的方法。参数attempt是每次尝试的记录。

public class MyRetryListener<Boolean> implements RetryListener {

    @Override
    public <Boolean> void onRetry(Attempt<Boolean> attempt) {

        // 第几次重试,(注意:第一次重试其实是第一次调用)
        System.out.print("[retry]time=" + attempt.getAttemptNumber());

        // 距离第一次重试的延迟
        System.out.print(",delay=" + attempt.getDelaySinceFirstAttempt());

        // 重试结果: 是异常终止, 还是正常返回
        System.out.print(",hasException=" + attempt.hasException());
        System.out.print(",hasResult=" + attempt.hasResult());

        // 是什么原因导致异常
        if (attempt.hasException()) {
            System.out.print(",causeBy=" + attempt.getExceptionCause().toString());
        } else {
            // 正常返回时的结果
            System.out.print(",result=" + attempt.getResult());
        }

        // bad practice: 增加了额外的异常处理代码
        try {
            Boolean result = attempt.get();
            System.out.print(",rude get=" + result);
        } catch (ExecutionException e) {
            System.err.println("this attempt produce exception." + e.getCause().toString());
        }

        System.out.println();
    }
}

增加监听者(可以增加多个,顺序执行)

.withRetryListener(new MyRetryListener<>())
.withRetryListener(new RetryListener() {
            @Override
            public <V> void onRetry(Attempt<V> attempt) {
                if (attempt.hasException()){
                    attempt.getExceptionCause().printStackTrace();
                }
            }
        })

核心逻辑

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

主要接口

Attempt

一次执行任务

AttemptTimeLimiter

单次任务执行时间限制(如果单次任务执行超时,则终止执行当前任务)

BlockStrategies

任务阻塞策略(通俗的讲就是当前任务执行完,下次任务还没开始这段时间做什么)

默认策略为:BlockStrategies.THREAD_SLEEP_STRATEGY 也就是调用 Thread.sleep(sleepTime);

RetryException

重试异常

RetryListener

自定义重试监听器,可以用于异步记录错误日志;

StopStrategy

停止重试策略,提供三种

  • StopAfterDelayStrategy :设定一个最长允许的执行时间比如设定最长执行10s,无论任务执行次数,只要重试的时候超出了最长时间,则任务终止,并返回重试异常RetryException
  • NeverStopStrategy :不停止,用于需要一直轮训知道返回期望结果的情况
  • StopAfterAttemptStrategy :设定最大重试次数,如果超出最大重试次数则停止重试,并返回重试异常

WaitStrategy

等待时长策略(控制时间间隔),返回结果为下次执行时长

  • FixedWaitStrategy:固定等待时长策略
  • RandomWaitStrategy:随机等待时长策略(可以提供一个最小和最大时长,等待时长为其区间随机值)
  • IncrementingWaitStrategy:递增等待时长策略(提供一个初始值和步长,等待时间随重试次数增加而增加)
  • ExponentialWaitStrategy:指数等待时长策略
  • FibonacciWaitStrategy :Fibonacci 等待时长策略
  • ExceptionWaitStrategy :异常时长等待策略
  • CompositeWaitStrategy :复合时长等待策略
目录
相关文章
|
XML Java Maven
【Maven技术专题】「实战开发系列」盘点Maven项目中打包需要注意到的那点事儿
【Maven技术专题】「实战开发系列」盘点Maven项目中打包需要注意到的那点事儿
322 1
|
11月前
|
Java API Spring
Java实现异步编程的几种方式
通过本文的介绍,我们了解了在Java中实现异步编程的几种常用方式。每种方法都有其优点和适用场景,具体选择哪种方式应根据实际需求和场景决定。如果任务较简单,可以使用 `Thread`或 `ExecutorService`;如果需要处理复杂的异步流程,可以考虑使用 `CompletableFuture`或Reactive编程框架。希望本文对您理解和实现Java异步编程有所帮助。
412 1
|
缓存 Java Maven
深入解析Google Guava库与Spring Retry重试框架
深入解析Google Guava库与Spring Retry重试框架
|
机器学习/深度学习 人工智能 机器人
人工智能与自动化:重塑未来工作场景
【8月更文第8天】随着技术的飞速发展,人工智能(AI)和自动化已成为推动各行各业变革的关键力量。这些技术不仅提高了生产效率,还为传统工作岗位带来了新的活力,并创造出了许多全新的职业领域。本文将探讨AI和自动化如何重塑工作场景,并通过具体的编程示例来展示如何利用这些技术。
416 1
|
9月前
|
传感器 监控 数据挖掘
Flink 四大基石之 Time (时间语义) 的使用详解
Flink 中的时间分为三类:Event Time(事件发生时间)、Ingestion Time(数据进入系统时间)和 Processing Time(数据处理时间)。Event Time 通过嵌入事件中的时间戳准确反映数据顺序,支持复杂窗口操作。Watermark 机制用于处理 Event Time,确保数据完整性并触发窗口计算。Flink 还提供了多种迟到数据处理方式,如默认丢弃、侧输出流和允许延迟处理,以应对不同场景需求。掌握这些时间语义对编写高效、准确的 Flink 应用至关重要。
501 21
|
JSON fastjson Java
Spring Boot Jackson 和Fast JSON 用哪个好啊 ?
【4月更文挑战第22天】
2663 1
|
消息中间件 监控 Java
Java一分钟之-Spring Integration:企业级集成
【6月更文挑战第11天】Spring Integration是Spring框架的一部分,用于简化企业应用的集成,基于EIP设计,采用消息传递连接不同服务。核心概念包括通道(Channel)、端点(Endpoint)和适配器(Adapter)。常见问题涉及过度设计、消息丢失与重复处理、性能瓶颈。解决策略包括遵循YAGNI原则、使用幂等性和事务管理、优化线程配置。通过添加依赖并创建简单消息处理链,可以开始使用Spring Integration。注意实践中要关注消息可靠性、系统性能,逐步探索高级特性以提升集成解决方案的质量和可维护性。
347 3
Java一分钟之-Spring Integration:企业级集成
|
Python
pyinstaller 安装使用
【7月更文挑战第8天】
887 3
|
消息中间件 监控 数据可视化
【时序数据库InfluxDB】Windows环境下配置InfluxDB+数据可视化,以及使用 C#进行简单操作的代码实例
influxDB的官网下载地址 https://portal.influxdata.com/downloads/打开以后,如下图所示,可以选择版本号,以及平台。此处咱们选择windows平台。不过此处没有实际的可以下载的地方,着实比较过分,不过咱们可以另辟蹊径。
1982 0
【时序数据库InfluxDB】Windows环境下配置InfluxDB+数据可视化,以及使用 C#进行简单操作的代码实例
|
算法 Oracle Java
一文详解|从JDK8飞升到JDK17,再到未来的JDK21
本文深入浅出地解析了从JDK8到JDK17版本升级的新特性,并展望后续将会更新的JDK21.
11066 8