java之重试

在开发中时常会遇到多次“尝试”请求,例如请求一个接口如果因为偶尔一次网络抖动导致失败,或者轮询查询某条数据的动态。这时候就要用到重复下发的逻辑。

常见简单的重复下发逻辑如下:

@Test
    public void test001()  {
        boolean res = false;
        do {
            res = sendDataByRandom(10);
        }while (!res);
    }
    public boolean sendDataByRandom(int i) {
        Random r = new Random();
        int val = r.nextInt(i);
        System.out.println(val);
        return val==(i-1);
    }

如果需要限制次数,则加一个计数器进入while判断的里面。

找不到理由介绍下面的重试框架了, 那就生硬的介绍下重试框架。

本篇文章介绍两个重试框架:

1、Spring Retry重试框架;
2、Guava Retry重试框架;

Spring Retry重试框架

首先引入jar包:

<!-- https://mvnrepository.com/artifact/org.springframework.retry/spring-retry -->
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.3.1</version>
</dependency>

该框架包含如下重试策略。

NoBackOffPolicy:无退避算法策略,每次失败时立即重试

FixedBackOffPolicy:固定时间的退避策略,需设置参数sleeper和backOffPeriod,sleeper指定等待策略,默认是Thread.sleep,即线程休眠,backOffPeriod指定休眠时间,默认1秒

UniformRandomBackOffPolicy:随机时间退避策略,需设置sleeper、minBackOffPeriod和maxBackOffPeriod,该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间,minBackOffPeriod默认500毫秒,maxBackOffPeriod默认1500毫秒

ExponentialBackOffPolicy:指数退避策略,需设置参数sleeper、initialInterval、maxInterval和multiplier,initialInterval指定初始休眠时间,默认100毫秒,maxInterval指定最大休眠时间,默认30秒,multiplier指定乘数,即下一次休眠时间为当前休眠时间*multiplier

ExponentialRandomBackOffPolicy:随机指数退避策略,引入随机乘数可以实现随机乘数回退

名词解释:

退避算法策略:重试之间的间隔,类似技能冷却时间。

NoBackOffPolicy

@Test
    public void NoBackOffPolicy()  {
        Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();
        //  key表示异常的类,val标识是否重试 true:重试  false:不重试
        // 此处异常可以自定义
        exceptionMap.put(RemoteAccessException.class,true);
        // 构建重试模板实例
        RetryTemplate retryTemplate = new RetryTemplate();
        // 设置重试回退操作策略,主要设置重试间隔时间  NoBackOffPolicy 没有间隔时间重试
        NoBackOffPolicy noBackOffPolicy = new NoBackOffPolicy();
        // 设置重试策略,主要设置重试次数
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(5, exceptionMap);
        retryTemplate.setRetryPolicy(retryPolicy);
        retryTemplate.setBackOffPolicy(noBackOffPolicy);
        Boolean execute = retryTemplate.execute(
                //RetryCallback
                retryContext -> {
                    boolean b = sendDataByRandom(10);
                    if(!b){
                        throw new RemoteAccessException("请求重试");
                    }
                    log.info("调用的结果:{}", b);
                    return b;
                },
                retryContext -> {
                    //RecoveryCallback
                    log.info("已达到最大重试次数或抛出了不重试的异常~~~");
                    return false;
                }
        );

        log.info("执行结果:{}",execute);
    }

 

 

public final <T, E extends Throwable> T execute(
RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback) throws E {
        return this.doExecute(retryCallback, recoveryCallback, (RetryState)null);
    }

只摘抄这个重构方法,两个函数接口为参数,

可以理解为第一个为执行的方法,主要逻辑。第二个参数为重试超过上限的回调的方法。

整体逻辑如下:

// 创建模板
RetryTemplate retryTemplate = new RetryTemplate();
// 创建退避策略
BackOffPolicy noBackOffPolicy = new NoBackOffPolicy();
// ?如果有退避则略,则设置退避间隔 // backOffPolicy.setBackOffPeriod(fixedPeriodTime);
//创建重试策略与次数
Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>();
//  key表示异常的类,val标识是否重试 true:重试  false:不重试
// 此处异常可以自定义
exceptionMap.put(RemoteAccessException.class,true);
// 设置重试策略,主要设置重试次数
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(5, exceptionMap);

// 添加配置
retryTemplate.setRetryPolicy(retryPolicy);
retryTemplate.setBackOffPolicy(noBackOffPolicy);

 // Boolean execute = retryTemplate.execute(@执行逻辑,@超出最大尝试次数处理逻辑)

 监听器

   // 第一次重试的时候会执行该方法
<T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);
   // 重试结束后会调用改方法
<T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);
   // 每次重试产生异常时会调用改方法
<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);

终止方法

① 因为是抛出异常重试,不抛出异常就不会触发重试了。

//                    throw new RemoteAccessException("请求重试");

② 修改轮询数

retryPolicy.setMaxAttempts(1);

把最大轮询数调小 

 

 

 Guava Retry重试框架

jar包引入

    <!-- 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>

 代码实现

 public static void main(String[] args) {
        // RetryerBuilder 构建重试实例 retryer,可以设置重试源且可以支持多个重试源,可以配置重试次数或重试超时时间,以及可以配置等待时间间隔
        Retryer<Boolean> retryer = RetryerBuilder.<Boolean> newBuilder()
                // 异常重试
                .retryIfExceptionOfType(RuntimeException.class).retryIfExceptionOfType(NumberFormatException.class)
                // 结果重试
                .retryIfResult(res-> res==false)
                // 等待间隔时间
                .withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))
                // 重试次数
                .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                .withRetryListener(new RetryListener() {
                    @Override
                    public <V> void onRetry(Attempt<V> attempt) {
                        System.out.println("次数:"+attempt.getAttemptNumber());
                    }
                })
                .build();
        try {
            retryer.call(() ->  {
                System.out.println("执行任务");
                return false;
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

 

 

 

 

 

 

 

 

 

 

文章参考:https://zzzgd.blog.csdn.net/article/details/84377962

 

posted @ 2021-11-08 14:38  苦心明  阅读(1034)  评论(0)    收藏  举报