使用Guava Retrying优雅的实现业务异常重试

上次写过一篇如何使用spring retry来实现业务重试的文章:https://blog.csdn.net/Kingsea442/article/details/135341747

尽管 Spring Retry 工具能够优雅地实现重试,但它仍然存在两个不太友好的设计:

  1. 重试实体被限定为 Throwable 子类,这意味着重试针对的是可捕获的功能异常,但实际上我们可能希望依赖某个数据对象实体作为重试实体,但是 Spring Retry 框架必须强制将其转换为 Throwable 子类。
  2. 重试根源的断言对象使用的是 doWithRetry 的 Exception 异常实例,这不符合正常内部断言的返回设计。
    Spring Retry 建议使用注解来对方法进行重试,重试逻辑是同步执行的。重试的"失败"是指 Throwable 异常,如果你要通过返回值的某个状态来判断是否需要重试,则可能需要自己判断返回值并手动抛出异常。

Guava Retrying

开源地址:https://github.com/rholder/guava-retrying

Guava Retrying 是基于 Google 的核心类库 Guava 的重试机制实现的一个库,它提供了一种通用方法,可以使用 Guava 谓词匹配增强的特定停止、重试和异常处理功能来重试任意 Java 代码。这个库支持多种重试策略,比如指定重试次数、指定重试时间间隔等。此外,它还支持谓词匹配来确定是否应该重试,以及在重试时应该做些什么。Guava Retrying 的最大特点是它能够灵活地与其他 Guava 类库集成,这使得它非常易于使用。

pom依赖:

xml 复制代码
	// maven依赖
	<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>2.0.0</version>
    </dependency>
	
	// gradle依赖:
	compile "com.github.rholder:guava-retrying:2.0.0"

测试demo

java 复制代码
public static void main(String[] args) {
     Callable<Boolean> callable = new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                // do something useful here
                log.info("call...");
                throw new RuntimeException();
            }
        };

        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
             //retryIf 重试条件
                .retryIfException()
                .retryIfRuntimeException()
                .retryIfExceptionOfType(Exception.class)
                .retryIfException(Predicates.equalTo(new Exception()))
                .retryIfResult(Predicates.equalTo(false))
           //等待策略:每次请求间隔1s
                .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
          //停止策略 : 尝试请求6次
              .withStopStrategy(StopStrategies.stopAfterAttempt(6))
                //时间限制 : 某次请求不得超过2s 
              .withAttemptTimeLimiter(
          AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS))
           //注册一个自定义监听器(可以实现失败后的兜底方法)
              .withRetryListener(new MyRetryListener()).build();
        try {
            retryer.call(callable);
        } catch (Exception ee) {
            ee.printStackTrace();
        }
}

当发生重试之后,假如我们需要做一些额外的处理动作,比如发个告警邮件啥的,那么可以使用RetryListener。每次重试之后,guava-retrying会自动回调我们注册的监听。可以注册多个RetryListener,会按照注册顺序依次调用。

java 复制代码
public class MyRetryListener implements RetryListener {
    @Override
    public <V> void onRetry(Attempt<V> 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());
            // do something useful here
        } else {
            // 正常返回时的结果
            System.out.print(",result=" + attempt.getResult());
        }
        System.out.println();
    }
}

RetryerBuilder是一个factory创建者,可以定制设置重试源且可以支持多个重试源,可以配置重试次数或重试超时时间,以及可以配置等待时间间隔,创建重试者Retryer实例。

RetryerBuilder的重试源支持Exception异常对象 和自定义断言对象,通过retryIfException 和retryIfResult设置,同时支持多个且能兼容。

retryIfException,抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。

retryIfRuntimeException只会在抛runtime异常的时候才重试,checked异常和error都不重试。

retryIfExceptionOfType允许我们只在发生特定异常的时候才重试,比如NullPointerException和IllegalStateException都属于runtime异常,也包括自定义的error。

retryIfResult可以指定你的Callable方法在返回值的时候进行重试。

StopStrategy:停止重试策略,提供以下方式:

StopAfterDelayStrategy: 设定一个最长允许的执行时间;比如设定最长执行10s,无论任务执行次数,只要重试的时候超出了最长时间,则任务终止,并返回重试异常
NeverStopStrategy: 用于需要一直轮训知道返回期望结果的情况。
StopAfterAttemptStrategy: 设定最大重试次数,如果超出最大重试次数则停止重试,并返回重试异常。
WaitStrategy: 等待时长策略(控制时间间隔)
FixedWaitStrategy: 固定等待时长策略。
RandomWaitStrategy: 随机等待时长策略(可以提供一个最小和最大时长,等待时长为其区间随机值)
IncrementingWaitStrategy: 递增等待时长策略(提供一个初始值和步长,等待时间随重试次数增加而增加)。
ExponentialWaitStrategy: 指数等待时长策略
FibonacciWaitStrategy: 等待时长策略
ExceptionWaitStrategy: 异常时长等待策略
CompositeWaitStrategy: 复合时长等待策略

总结

Guava Retryer 工具与 Spring Retry 类似,都是通过定义重试者角色来包装正常逻辑重试。然而,Guava Retryer 在策略定义方面更优秀。它不仅支持设置重试次数和重试频度控制,还能够兼容多个异常或自定义实体对象的重试源定义,从而提供更多的灵活性。这使得 Guava Retryer 能够适用于更多的业务场景,比如网络请求、数据库访问等。此外,Guava Retryer 还具有很好的可扩展性,可以很方便地与其他 Guava 类库集成使用。

相关推荐
侠客行03174 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪4 小时前
深入浅出LangChain4J
java·langchain·llm
老毛肚5 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎6 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
Yvonne爱编码6 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚6 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂6 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
fuquxiaoguang6 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
琹箐6 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
__WanG7 小时前
JavaTuples 库分析
java