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

浙公网安备 33010602011771号