广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java利用Guava Retry实现重处理
  • 217
分享到

Java利用Guava Retry实现重处理

2024-04-02 19:04:59 217人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录一、pom依赖二、使用示例三、重试时机1. 根据异常进行重试2. 根据返回结果进行重试四、停止重试策略StopStrategy1. NeverStopStrategy2. Sto

在日常开发中,尤其是在微服务盛行的时代下,我们在调用外部接口时,经常会因为第三方接口超时、限流等问题从而造成接口调用失败,那么此时我们通常会对接口进行重试,那么问题来了,如何重试呢?该重试几次呢?如果要设置重试时间超过多长时间后还不成功就不重试了该怎么做呢?所幸guava-retrying为我们提供了强大而简单易用的重试框架guava-retrying。

guava-retrying是谷歌的Guava库的一个小扩展,允许为任意函数调用创建可配置的重试策略,比如与正常运行时间不稳定的远程服务对话的函数调用。

一、pom依赖

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

二、使用示例

我们可以通过RetryerBuilder来构造一个重试器,通过RetryerBuilder可以设置什么时候需要重试(即重试时机)、停止重试策略、失败等待时间间隔策略、任务执行时长限制策略

先看一个简单的例子:

private int invokeCount = 0;

    public int realAction(int num) {
        invokeCount++;
        System.out.println(String.fORMat("当前执行第 %d 次,num:%d", invokeCount, num));
        if (num <= 0) {
            throw new IllegalArgumentException();
        }
        return num;
    }

    @Test
    public void guavaRetryTest001() {
        Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
            // 非正数进行重试
            .retryIfRuntimeException()
            // 偶数则进行重试
            .retryIfResult(result -> result % 2 == 0)
            // 设置最大执行次数3次
            .withStopStrategy(StopStrategies.stopAfterAttempt(3)).build();

        try {
            invokeCount=0;
            retryer.call(() -> realAction(0));
        } catch (Exception e) {
            System.out.println("执行0,异常:" + e.getMessage());
        }

        try {
            invokeCount=0;
            retryer.call(() -> realAction(1));
        } catch (Exception e) {
            System.out.println("执行1,异常:" + e.getMessage());
        }

        try {
            invokeCount=0;
            retryer.call(() -> realAction(2));
        } catch (Exception e) {
            System.out.println("执行2,异常:" + e.getMessage());
        }
    }

输出:

当前执行第 1 次,num:0
当前执行第 2 次,num:0
当前执行第 3 次,num:0
执行0,异常:Retrying failed to complete successfully after 3 attempts.
当前执行第 1 次,num:1
当前执行第 1 次,num:2
当前执行第 2 次,num:2
当前执行第 3 次,num:2
执行2,异常:Retrying failed to complete successfully after 3 attempts.

三、重试时机

RetryerBuilder的retryIfXXX()方法用来设置**在什么情况下进行重试,总体上可以分为根据执行异常进行重试和根据方法执行结果进行重试两类。关注公众号:“码猿技术专栏”,回复关键词:“1111” 获取阿里内部Java调优手册

1. 根据异常进行重试

2. 根据返回结果进行重试

retryIfResult(@Nonnull Predicate resultPredicate) 这个比较简单,当我们传入的resultPredicate返回true时则进行重试

四、停止重试策略StopStrategy

停止重试策略用来决定什么时候不进行重试,其接口com.github.rholder.retry.StopStrategy,停止重试策略的实现类均在com.github.rholder.retry.StopStrategies中,它是一个策略工厂类。

public interface StopStrategy {

    
    boolean shouldStop(Attempt failedAttempt);
}

1. NeverStopStrategy

此策略将永远重试,永不停止,查看其实现类,直接返回了false:

@Override
public boolean shouldStop(Attempt failedAttempt) {
 return false;
}

2. StopAfterAttemptStrategy

当执行次数到达指定次数之后停止重试,查看其实现类:

private static final class StopAfterAttemptStrategy implements StopStrategy {
        private final int maxAttemptNumber;

        public StopAfterAttemptStrategy(int maxAttemptNumber) {
            Preconditions.checkArgument(maxAttemptNumber >= 1, "maxAttemptNumber must be >= 1 but is %d", maxAttemptNumber);
            this.maxAttemptNumber = maxAttemptNumber;
        }

        @Override
        public boolean shouldStop(Attempt failedAttempt) {
            return failedAttempt.getAttemptNumber() >= maxAttemptNumber;
        }
    }

3. StopAfterDelayStrategy

当距离方法的第一次执行超出了指定的delay时间时停止,也就是说一直进行重试,当进行下一次重试的时候会判断从第一次执行到现在的所消耗的时间是否超过了这里指定的delay时间,查看其实现:

private static final class StopAfterAttemptStrategy implements StopStrategy {
        private final int maxAttemptNumber;

        public StopAfterAttemptStrategy(int maxAttemptNumber) {
            Preconditions.checkArgument(maxAttemptNumber >= 1, "maxAttemptNumber must be >= 1 but is %d", maxAttemptNumber);
            this.maxAttemptNumber = maxAttemptNumber;
        }

        @Override
        public boolean shouldStop(Attempt failedAttempt) {
            return failedAttempt.getAttemptNumber() >= maxAttemptNumber;
        }
    }

五、重试间隔策略、重试阻塞策略

这两个策略放在一起说,它们合起来的作用就是用来控制重试任务之间的间隔时间,以及如何任务在等待时间间隔时如何阻塞。也就是说WaitStrategy决定了重试任务等待多久后进行下一次任务的执行,BlockStrategy用来决定任务如何等待。它们两的策略工厂分别为com.github.rholder.retry.WaitStrategies和BlockStrategies。

1.BlockStrategy

(1) ThreadSleepStrategy

这个是BlockStrategies,决定如何阻塞任务,其主要就是通过**Thread.sleep()**来进行阻塞的,查看其实现:

@Immutable
    private static class ThreadSleepStrategy implements BlockStrategy {

        @Override
        public void block(long sleepTime) throws InterruptedException {
            Thread.sleep(sleepTime);
        }
    }

2. WaitStrategy

(1) IncrementingWaitStrategy

该策略在决定任务间隔时间时,返回的是一个递增的间隔时间,即每次任务重试间隔时间逐步递增,越来越长,查看其实现:

private static final class IncrementingWaitStrategy implements WaitStrategy {
        private final long initialSleepTime;
        private final long increment;

        public IncrementingWaitStrategy(long initialSleepTime,
                                        long increment) {
            Preconditions.checkArgument(initialSleepTime >= 0L, "initialSleepTime must be >= 0 but is %d", initialSleepTime);
            this.initialSleepTime = initialSleepTime;
            this.increment = increment;
        }

        @Override
        public long computeSleepTime(Attempt failedAttempt) {
            long result = initialSleepTime + (increment * (failedAttempt.getAttemptNumber() - 1));
            return result >= 0L ? result : 0L;
        }
    }

该策略输入一个起始间隔时间值和一个递增步长,然后每次等待的时长都递增increment时长。

(2) RandomWaitStrategy

顾名思义,返回一个随机的间隔时长,我们需要传入的就是一个最小间隔和最大间隔,然后随机返回介于两者之间的一个间隔时长,其实现为:

private static final class RandomWaitStrategy implements WaitStrategy {
        private static final Random RANDOM = new Random();
        private final long minimum;
        private final long maximum;

        public RandomWaitStrategy(long minimum, long maximum) {
            Preconditions.checkArgument(minimum >= 0, "minimum must be >= 0 but is %d", minimum);
            Preconditions.checkArgument(maximum > minimum, "maximum must be > minimum but maximum is %d and minimum is", maximum, minimum);

            this.minimum = minimum;
            this.maximum = maximum;
        }

        @Override
        public long computeSleepTime(Attempt failedAttempt) {
            long t = Math.abs(RANDOM.nextLong()) % (maximum - minimum);
            return t + minimum;
        }
    }

(3) FixedWaitStrategy

该策略是返回一个固定时长的重试间隔。查看其实现:

private static final class FixedWaitStrategy implements WaitStrategy {
        private final long sleepTime;

        public FixedWaitStrategy(long sleepTime) {
            Preconditions.checkArgument(sleepTime >= 0L, "sleepTime must be >= 0 but is %d", sleepTime);
            this.sleepTime = sleepTime;
        }

        @Override
        public long computeSleepTime(Attempt failedAttempt) {
            return sleepTime;
        }
    }

() ExceptionWaitStrategy

该策略是由方法执行异常来决定是否重试任务之间进行间隔等待,以及间隔多久。

private static final class ExceptionWaitStrategy<T extends Throwable> implements WaitStrategy {
        private final Class<T> exceptionClass;
        private final Function<T, Long> function;

        public ExceptionWaitStrategy(@Nonnull Class<T> exceptionClass, @Nonnull Function<T, Long> function) {
            this.exceptionClass = exceptionClass;
            this.function = function;
        }

        @SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "ConstantConditions", "unchecked"})
        @Override
        public long computeSleepTime(Attempt lastAttempt) {
            if (lastAttempt.hasException()) {
                Throwable cause = lastAttempt.getExceptionCause();
                if (exceptionClass.isAssignableFrom(cause.getClass())) {
                    return function.apply((T) cause);
                }
            }
            return 0L;
        }
    }

(5) CompositeWaitStrategy

这个没啥好说的,顾名思义,就是一个策略的组合,你可以传入多个WaitStrategy,然后所有WaitStrategy返回的间隔时长相加就是最终的间隔时间。查看其实现:

private static final class CompositeWaitStrategy implements WaitStrategy {
        private final List<WaitStrategy> waitStrategies;

        public CompositeWaitStrategy(List<WaitStrategy> waitStrategies) {
            Preconditions.checkState(!waitStrategies.isEmpty(), "Need at least one wait strategy");
            this.waitStrategies = waitStrategies;
        }

        @Override
        public long computeSleepTime(Attempt failedAttempt) {
            long waitTime = 0L;
            for (WaitStrategy waitStrategy : waitStrategies) {
                waitTime += waitStrategy.computeSleepTime(failedAttempt);
            }
            return waitTime;
        }
    }

(6) FibonacciWaitStrategy

这个策略与IncrementingWaitStrategy有点相似,间隔时间都是随着重试次数的增加而递增的,不同的是,FibonacciWaitStrategy是按照斐波那契数列来进行计算的,使用这个策略时,我们需要传入一个乘数因子和最大间隔时长,其实现就不贴了

(7) ExponentialWaitStrategy

这个与IncrementingWaitStrategy、FibonacciWaitStrategy也类似,间隔时间都是随着重试次数的增加而递增的,但是该策略的递增是呈指数级递增。查看其实现:

private static final class ExponentialWaitStrategy implements WaitStrategy {
        private final long multiplier;
        private final long maximumWait;

        public ExponentialWaitStrategy(long multiplier,
                                       long maximumWait) {
            Preconditions.checkArgument(multiplier > 0L, "multiplier must be > 0 but is %d", multiplier);
            Preconditions.checkArgument(maximumWait >= 0L, "maximumWait must be >= 0 but is %d", maximumWait);
            Preconditions.checkArgument(multiplier < maximumWait, "multiplier must be < maximumWait but is %d", multiplier);
            this.multiplier = multiplier;
            this.maximumWait = maximumWait;
        }

        @Override
        public long computeSleepTime(Attempt failedAttempt) {
            double exp = Math.pow(2, failedAttempt.getAttemptNumber());
            long result = Math.round(multiplier * exp);
            if (result > maximumWait) {
                result = maximumWait;
            }
            return result >= 0L ? result : 0L;
        }
    }

六、重试监听器RetryListener

当发生重试时,将会调用RetryListener的onRetry方法,此时我们可以进行比如记录日志等额外操作。

public int realAction(int num) {
        if (num <= 0) {
            throw new IllegalArgumentException();
        }
        return num;
    }

    @Test
    public void guavaRetryTest001() throws ExecutionException, RetryException {
        Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder().retryIfException()
            .withRetryListener(new MyRetryListener())
            // 设置最大执行次数3次
            .withStopStrategy(StopStrategies.stopAfterAttempt(3)).build();
        retryer.call(() -> realAction(0));

    }

    private static class MyRetryListener implements RetryListener {

        @Override
        public <V> void onRetry(Attempt<V> attempt) {
            System.out.println("第" + attempt.getAttemptNumber() + "次执行");
        }
    }

输出:

第1次执行
第2次执行
第3次执行

七、重试原理

其实到这一步之后,实现原理大概就很清楚了,就是由上述各种策略配合从而达到了非常灵活的重试机制。在这之前我们看一个上面没说的东东-Attempt:

public interface Attempt<V> {
    public V get() throws ExecutionException;

    public boolean hasResult();
    
    public boolean hasException();

    public V getResult() throws IllegalStateException;

    public Throwable getExceptionCause() throws IllegalStateException;

    public long getAttemptNumber();

    public long getDelaySinceFirstAttempt();
}

通过接口方法可以知道Attempt这个类包含了任务执行次数、任务执行异常、任务执行结果、以及首次执行任务至今的时间间隔,那么我们后续的不管重试时机、还是其他策略都是根据此值来决定。

接下来看关键执行入口Retryer##call:

public V call(Callable<V> callable) throws ExecutionException, RetryException {
        long startTime = System.nanoTime();
        
        // 执行次数从1开始
        for (int attemptNumber = 1; ; attemptNumber++) {
            Attempt<V> attempt;
            try {
                // 尝试执行
                V result = attemptTimeLimiter.call(callable);
                
                // 执行成功则将结果封装为ResultAttempt
                attempt = new Retryer.ResultAttempt<V>(result, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
            } catch (Throwable t) {
                // 执行异常则将结果封装为ExceptionAttempt
                attempt = new Retryer.ExceptionAttempt<V>(t, attemptNumber, TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
            }

            // 这里将执行结果传给RetryListener做一些额外事情
            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);
                }
            }
        }

八、总结

通篇下来可以看到其实核心实现并不难,但是此框架通过建造者模式和策略模式组合运用,提供了十分清晰明了且灵活的重试机制,其设计思路还是值得借鉴学习

以上就是Java利用Guava Retry实现重处理的详细内容,更多关于Java Guava Retry的资料请关注编程网其它相关文章!

--结束END--

本文标题: Java利用Guava Retry实现重处理

本文链接: https://www.lsjlt.com/news/166057.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java利用Guava Retry实现重处理
    目录一、pom依赖二、使用示例三、重试时机1. 根据异常进行重试2. 根据返回结果进行重试四、停止重试策略StopStrategy1. NeverStopStrategy2. Sto...
    99+
    2022-11-13
  • 如何在java利用tostring方法实现重写
    今天就跟大家聊聊有关如何在java利用tostring方法实现重写,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。当需要将一个对象输出到显示器时,通常要调用他的toString()方法...
    99+
    2023-05-31
    java tostring ava
  • 怎么在java中利用ReentrantLock实现重入测试
    本篇文章给大家分享的是有关怎么在java中利用ReentrantLock实现重入测试,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java的特点有哪些Java的特点有哪些1.J...
    99+
    2023-06-14
  • Java利用MD5加盐实现对密码进行加密处理
    目录前言Java常用加密手段场景加密手段应用场景MD5加密的风险什么是盐引入MD5工具类编写MD5加盐工具类测试盐加密强度注册加密登录解密总结前言 在开发的时候,有一些敏感信息是不能...
    99+
    2023-02-22
    Java MD5 密码加密 Java 密码加密 Java MD5 Java 加密
  • Java怎么利用MD5实现对密码进行加密处理
    本文小编为大家详细介绍“Java怎么利用MD5实现对密码进行加密处理”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java怎么利用MD5实现对密码进行加密处理”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Jav...
    99+
    2023-07-05
  • 如何在 Windows 环境下利用 Java Spring 实现实时数据处理?
    Java Spring 是一款流行的开源框架,用于构建企业级应用程序。它是一个轻量级的框架,提供了大量的组件和工具,可以简化开发过程,提高开发效率。在本文中,我们将介绍如何在 Windows 环境下利用 Java Spring 实现实时数...
    99+
    2023-10-01
    spring 实时 windows
  • Java学习笔记:如何利用对象实现实时数据处理?
    随着大数据时代的到来,实时数据处理变得越来越重要。在Java中,我们可以利用对象实现实时数据处理。本篇文章将介绍如何使用Java对象来实现实时数据处理,并提供相应的演示代码。 一、Java对象 Java对象是Java程序中的基本单元,它是...
    99+
    2023-09-24
    学习笔记 对象 实时
  • Python利用PyMuPDF实现PDF文件处理
    目录1、PyMuPDF简介介绍功能2、安装关于命名fitz的说明3、使用方法导入库,查看版本打开文档Document的方法和属性获取元数据获取目标大纲页面(Page)PDF操作1、P...
    99+
    2022-11-11
  • Linux下如何利用Java进行自然语言处理和重定向?
    自然语言处理是一门涉及到计算机科学、人工智能和语言学的交叉学科。它的目的是让计算机能够理解、处理和生成自然语言文本。在Linux下,我们可以使用Java来进行自然语言处理。本文将介绍如何使用Java进行自然语言处理以及如何利用重定向进行输...
    99+
    2023-10-10
    重定向 linux 自然语言处理
  • Java 中如何利用 Linux 系统实现对象重定向?
    在 Java 程序中,我们可以使用 System.out.println() 方法输出信息到控制台,但在一些情况下,我们需要将输出信息重定向到其他地方,如文件、网络等。本文将介绍如何利用 Linux 系统实现 Java 中的对象重定向。 ...
    99+
    2023-10-10
    重定向 linux 对象
  • 如何利用python实现图片批处理
    前言 在训练神经网络之前,我们往往需要对数据集进行批量处理。本文以图片为例,介绍如何使用python实现图片的批量处理,包括批量命名,批量更改图像像素,批量对图片进行Harris、C...
    99+
    2022-11-12
  • Python利用Selenium实现弹出框的处理
    目录JavaScript三种弹出对话框的简单介绍alert() - 警告框confirm() - 确认框cprompt() - 提示框selenium 处理弹出对话框的常用方法sel...
    99+
    2022-11-11
  • 利用java实现动态代理的方法
    这篇文章将为大家详细讲解有关利用java实现动态代理的方法,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。java 动态代理的方法总结AOP的拦截功能是由java中的动态代理来实现的。说白了,...
    99+
    2023-05-31
    java 动态代理 ava
  • 如何利用Java中的重定向对象进行自然语言处理?
    自然语言处理(NLP)是人工智能领域中的一个重要分支,它旨在使计算机能够理解、解析和生成人类语言。Java语言作为一种广泛使用的编程语言,提供了许多用于NLP的工具和库。本文将介绍Java中的重定向对象,并演示如何使用它们进行自然语言处理。...
    99+
    2023-07-26
    重定向 对象 自然语言处理
  • 如何使用批处理实现反复重启
    这篇文章主要介绍了如何使用批处理实现反复重启,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。反复重启的批处理   @echo off shutdo...
    99+
    2023-06-09
  • 利用Python实现简单的验证码处理
    目录序言环境模块代码展示完整代码序言 我们在做采集数据的时候,过快或者访问频繁,或者一访问就给弹出验证码,然后就蚌珠了~ 今天就给大家来一个简单处理验证码的方法 环境模块 这里需要用...
    99+
    2022-11-11
  • JavaBean中怎么利用JDBC实现事务处理
    今天就跟大家聊聊有关JavaBean中怎么利用JDBC实现事务处理,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。JDBC(Java Data Base Connectivity,ja...
    99+
    2023-06-17
  • 如何利用Java语言实现高效的实时数据处理和NumPy并发?
    随着科技的发展和数据量的不断增加,实时数据处理成为了一个越来越重要的问题。而Java语言作为一种高效的编程语言,已经成为了数据处理领域中的佼佼者。本文将介绍如何利用Java语言实现高效的实时数据处理和NumPy并发,并给出实际的演示代码。 ...
    99+
    2023-09-23
    实时 numy 并发
  • 利用Java如何实现替换int数组中的重复数据
    本篇文章为大家展示了利用Java如何实现替换int数组中的重复数据,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。具体如下:package test;import java.util.HashSet;...
    99+
    2023-05-31
    java 数组 ava
  • 怎么在java项目中利用List对象列表实现去重
    这篇文章给大家介绍怎么在java项目中利用List对象列表实现去重,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、list去重1.1 实体类StudentList<Student>容量10k以上,要求去重...
    99+
    2023-05-31
    java list ava
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作