Retry轮询重试

Retry轮询重试

在某些容易超时的业务场景下,通常会按照主动提交+主动轮询提交结果的模式去设计,即先将需要的信息提交到第三方接口,由于第三方接口处理较慢,无法在提交接口立即返回结果,则会返回流水号,根据流水号再去轮询查询第三方接口流水号对应的提交结果,轮询重试需要有一定的间隔时间,让第三方有充分时间进行处理。

轮询重试这个场景,比较原始的做法是,循环+Sleep,例如:

for(int i = 0;i<n;i++){
	//查询
	//判断是否查询到终态结果
	//查询到终态则break,未查询到终态则继续
	//Thread.sleep() 休眠一定时间
}

市面上已经有比较成熟的组件可以胜任这样的场景,这里比较推荐 guava-retry

  1. 引入依赖

    <!-- 重试Retry组件-->
        <dependency>
          <groupId>com.github.rholder</groupId>
          <artifactId>guava-retrying</artifactId>
          <version>2.0.0</version>
        </dependency>
    
  2. 配置重试条件

    package com.example.demo.utils;
    
    import com.github.rholder.retry.Attempt;
    import com.github.rholder.retry.RetryListener;
    import com.github.rholder.retry.Retryer;
    import com.github.rholder.retry.RetryerBuilder;
    import com.github.rholder.retry.StopStrategies;
    import com.github.rholder.retry.WaitStrategies;
    import com.google.common.base.Predicates;
    import java.util.concurrent.Callable;
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @Description:重试工具类
     * @Author: 
     * @Date: 2022/10/10 10:59
     */
    public class RetryUtil {
    
        /**
         * 默认重试次数3次
         */
        private int maxAttempts = 3;
    
        /**
         * 默认等待时间3秒
         */
        private int waitTime = 3;
    
        /**
         * 默认重试等待时间单位 :秒
         */
        private TimeUnit timeUnit = TimeUnit.SECONDS;
    
        /**
         * 默认重试拦截器
         */
        private RetryListener retryListener = new MyRetryListener();
    
    
        private RetryUtil config(int maxAttempts,int waitTime){
            this.maxAttempts=maxAttempts;
            this.waitTime=waitTime;
            return this;
        }
    
    
        private Retryer init(){
            Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                    //设置发生异常则重试
                    .retryIfException()
                    //设置返回false则重试
                    .retryIfResult(Predicates.<Boolean>equalTo(Boolean.FALSE))
                    //设置重试等待时间及单位
                    .withWaitStrategy(WaitStrategies.fixedWait(waitTime,timeUnit))
                    //设置最大重试次数
                    .withStopStrategy(StopStrategies.stopAfterAttempt(maxAttempts))
                    //设置重试拦截器
                    .withRetryListener(retryListener)
                    .build();
    
            return retryer;
    
        }
    
        public static void retry(Callable callable,int maxAttempts,int waitTime){
            CompletableFuture.runAsync(() -> {
                Retryer retryer = new RetryUtil().config(maxAttempts, waitTime).init();
                try {
                    retryer.call(callable);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            },ExecutorsUtil.executorService);
        }
    
        /**
         * 重试拦截器
         */
        class MyRetryListener implements RetryListener{
    
            /**
             * 重试拦截记录日志
             */
            @Override
            public <V> void onRetry(Attempt<V> attempt) {
                long retryNumber = attempt.getAttemptNumber();
                if(attempt.hasException()){
                    //异常重试记录
                    Throwable exceptionCause = attempt.getExceptionCause();
                    System.out.println("第" + retryNumber + "次重试调用,发生异常:" + exceptionCause.toString());
                }else{
                    //正常重试记录
                    V result = attempt.getResult();
                    System.out.println("第" + retryNumber + "次重试调用,结果为:" + result);
                }
            }
        }
    }
    
    
    

    重点在init() 内部配置重试策略这里,可以设置重试的条件、重试的间隔时间、重试的最大次数、重试拦截器,不仅是例子中的策略,这个组件还提供了更多多样的策略,读者可以下去自己试一下

  3. 跑一下

    public static void main(String[] args){
            long begin = System.currentTimeMillis();
            System.out.println("主线程名" + Thread.currentThread().getName());
    
            RetryUtil.retry(() -> {
                int randomInt = RandomUtil.randomInt(0, 10);
                System.out.println("重试任务获取随机数" + randomInt);
                System.out.println("重试任务线程名" + Thread.currentThread().getName());
                return randomInt > 4;
            },3,3);
    
            long end = System.currentTimeMillis();
            System.out.println("执行耗时:" + String.valueOf(end-begin));
    
            /**
             * 输出:
             * 主线程名main
             * 执行耗时:69
             * 重试任务获取随机数0
             * 重试任务线程名pool-1-thread-1
             * 第1次重试调用,结果为:false
             * 重试任务获取随机数8
             * 重试任务线程名pool-1-thread-1
             * 第2次重试调用,结果为:true
             */
    
        }
    

    重试任务即随机取(0,10]之前的数,然后判断是否大于4,如果不满足条件,返回false,就会重试。

    分析输出的信息,重试任务设置的是异步执行,所以是由另一个线程开启重试,第n重试及结果的日志,则是重试拦截器中的逻辑打印出来的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南窗木心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值