前言
在目前微服务开发盛行的大潮下,我们经常会使用Sentinel、豪猪哥等服务熔断功能,来避免对一个服务的一次性打击,那么当一些重要的业务发生服务熔断时,我们不可能单纯的就取消了某个业务,而是希望它能够自动重试并且能够给我们打印出一些重试日志来。
我们可以通过简单的if-else语句 + for语句进行不断的重试,那么所有需要重试的语句都加上if、for语句正常人肯定都不喜欢这样的代码。而今天介绍的guava-retrying就是一个简单、优雅的重试组件库。
如何使用
- Maven依赖
<!-- guava-retrying -->
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
注意:当有其他依赖使用了低版本的guava的话会冲突报错,解决方法就是将低版本guava排除或者引入guava19.0版本即可。
使用这个组建库最核心的地方就是Retryer,而建造一个Retryer有三个维度需要我们掌握
何时重试(以retryIf开头)
在这个维度分两种情况1. 发生异常时重试 2. 返回结果满足断言时重试
public void guavaRetryTest01(){
Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
// 发生异常时重试
.retryIfException() // 发生任何异常时重试
.retryIfExceptionOfType(MySQLException.class) // 当发生异常isAssignedFrom 指定异常时重试
// 当指定范型的返回结果满足predicate时重试
.retryIfResult(result -> result % 2 == 0)
.build();
}
怎样重试(withXXXStrategy)
在retryer中策略分为三种类型:Stop、Wait、Block。分别对应着如何停下重试、等待多久、如何进行等待
每个类型对应着三个接口
@Test
public void guavaRetryTest02(){
Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
// 停止重试策略: stopAfterAttempt(int) 重试n次后不再重试 stopAfterDelay(long) 距离第一次执行超过n毫秒不再重试 neverStop() 用不停止
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
// 重试等待间隔策略: 决定每次重试的间隔: 参考WaitStrategy的实现类 最简单的两种 Random 和fixed
.withWaitStrategy(WaitStrategies.fixedWait(5, TimeUnit.SECONDS))
// 阻塞策略: 发生重试时如何阻塞 只有一个实现类ThreadSleep
.withBlockStrategy(BlockStrategies.threadSleepStrategy())
.build();
}
重试时做什么(RetryListener)
当发生重试时,我们希望使用监听器记录日志或者其他额外操作时,我们可以通过实现一个Lisnter来满足这样的需求。
通过RetryLisner接口 + RetryerBuidler.withRetryListener方法来实现这样的效果
@Test
public void guavaRetryTest03(){
Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
.withRetryListener(new CustomRetryerListener())
.build();
}
static class CustomRetryerListener implements RetryListener{
Logger logger = LoggerFactory.getLogger(ReteyerBuidlerDemo.class);
@Override
public <V> void onRetry(Attempt<V> attempt) {
logger.warn(getClass() + "第" + attempt.getAttemptNumber() + "次重试");
}
}
案例总结
综合上面的三个维度,我们就可以根据需要来进行重试了,比如我们要进行数据库的异步插入,一旦这个地方发生了个异常数据就丢失了,这时候我们可以通过重试框架进行。
- 我们希望数据库插入的时候返回值<1的时候,重隔3s进行重试,超过1分钟就不再重试了,并且打印一下日志
//mockdata
int retryCount = 0;
public Integer mapperInsert(){
return retryCount;
}
public void insert() throws ExecutionException, RetryException{
Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
.retryIfResult(integer -> integer == 0)
.withStopStrategy(StopStrategies.stopAfterDelay(30, TimeUnit.SECONDS))
.withWaitStrategy(WaitStrategies.fixedWait(5, TimeUnit.SECONDS))
.withRetryListener(new CustomRetryerListener())
.build();
retryer.call(() -> mapperInsert());
}
static class CustomRetryerListener implements RetryListener{
Logger logger = LoggerFactory.getLogger(ReteyerBuidlerDemo.class);
@Override
public <V> void onRetry(Attempt<V> attempt) {
logger.warn(getClass() + "第" + attempt.getAttemptNumber() + "次重试");
}
}
总结
以上就是这个guava-retrying组件库的使用方式了,通过建造者模式的retryer以达到消除if、for语句的效果。实现方式特别的简单。而且可以根据维度和策略来实现不同的效果。