二 重试框架之Guava-Retry
Guava retryer工具与spring-retry类似,都是通过定义重试者角色来包装正常逻辑重试,但是Guava retryer有更优的策略定义,在支持重试次数和重试频度控制基础上,能够兼容支持多个异常或者自定义实体对象的重试源定义,让重试功能有更多的灵活性。
Guava Retryer也是线程安全的,入口调用逻辑采用的是Java.util.concurrent.Callable的call方法,示例代码如下:
pom.xml加入依赖
<!-- 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>
更改一下测试的任务方法
package com.zgd.demo.thread.retry; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; import org.springframework.remoting.RemoteAccessException; /** * @Author: zgd * @Description: */ @Slf4j public class RetryDemoTask { /** * 重试方法 * @return */ public static boolean retryTask(String param) { log.info("收到请求参数:{}",param); int i = RandomUtils.nextInt(0,11); log.info("随机生成的数:{}",i); if (i < 2) { log.info("为0,抛出参数异常."); throw new IllegalArgumentException("参数异常"); }else if (i < 5){ log.info("为1,返回true."); return true; }else if (i < 7){ log.info("为2,返回false."); return false; }else{ //为其他 log.info("大于2,抛出自定义异常."); throw new RemoteAccessException("大于2,抛出自定义异常"); } } }
Guava
这里设定跟Spring-Retry不一样,我们可以根据返回的结果来判断是否重试,比如返回false我们就重试
package com.zgd.demo.thread.retry.guava; import com.github.rholder.retry.*; import com.zgd.demo.thread.retry.RetryDemoTask; import org.junit.Test; import org.springframework.remoting.RemoteAccessException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; /** * @Author: zgd * @Description: */ public class GuavaRetryTest { @Test public void fun01(){ // RetryerBuilder 构建重试实例 retryer,可以设置重试源且可以支持多个重试源,可以配置重试次数或重试超时时间,以及可以配置等待时间间隔 Retryer<Boolean> retryer = RetryerBuilder.<Boolean> newBuilder() .retryIfExceptionOfType(RemoteAccessException.class)//设置异常重试源 .retryIfResult(res-> res==false) //设置根据结果重试 .withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS)) //设置等待间隔时间 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) //设置最大重试次数 .build(); try { retryer.call(() -> RetryDemoTask.retryTask("abc")); } catch (Exception e) { e.printStackTrace(); } } }
运行测试一下
遇到了我们指定的需要重试的异常,进行重试,间隔是3秒
重试次数超过了最大重试次数
返回为true,直接结束重试
遇到了没有指定重试的异常,结束重试
返回false,重试
我们可以更灵活的配置重试策略,比如:
- retryIfException:
retryIfException,抛出runtime异常、checked异常时都会重试,但是抛出error不会重试。 - retryIfRuntimeException:
retryIfRuntimeException只会在抛runtime异常的时候才重试,checked异常和error都不重试。 - retryIfExceptionOfType:
retryIfExceptionOfType允许我们只在发生特定异常的时候才重试,比如NullPointerException和IllegalStateException都属于runtime异常,也包括自定义的error。
如:
retryIfExceptionOfType(NullPointerException.class)// 只在抛出空指针异常重试
- retryIfResult:
retryIfResult可以指定你的Callable方法在返回值的时候进行重试,如
// 返回false重试 .retryIfResult(Predicates.equalTo(false)) //以_error结尾才重试 .retryIfResult(Predicates.containsPattern("_error$")) //返回为空时重试 .retryIfResult(res-> res==null)
- RetryListener: 当发生重试之后,假如我们需要做一些额外的处理动作,比如log一下异常,那么可以使用
RetryListener。每次重试之后,guava-retrying会自动回调我们注册的监听。可以注册多个RetryListener,会按照注册顺序依次调用。
.withRetryListener(new RetryListener { @Override public <T> void onRetry(Attempt<T> attempt) { logger.error("第【{}】次调用失败" , attempt.getAttemptNumber()); } } )
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
总结
spring-retry 和 guava-retry 工具都是线程安全的重试,能够支持并发业务场景的重试逻辑正确性。两者都很好的将正常方法和重试方法进行了解耦,可以设置超时时间、重试次数、间隔时间、监听结果、都是不错的框架。
但是明显感觉得到,guava-retry在使用上更便捷,更灵活,能根据方法返回值来判断是否重试,而Spring-retry只能根据抛出的异常来进行重试。






