前言
在目前微服务开发盛行的大潮下,我们经常会使用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. 返回结果满足断言时重试
publicvoidguavaRetryTest01(){Retryer<Integer>retryer=RetryerBuilder.<Integer>newBuilder()//发生异常时重试.retryIfException()//发生任何异常时重试.retryIfExceptionOfType(MySQLException.class)//当发生异常isAssignedFrom指定异常时重试//当指定范型的返回结果满足predicate时重试.retryIfResult(result->result%2==0).build();}
怎样重试(withXXXStrategy)
在retryer中策略分为三种类型:Stop、Wait、Block。分别对应着如何停下重试、等待多久、如何进行等待
每个类型对应着三个接口
@TestpublicvoidguavaRetryTest02(){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方法来实现这样的效果
@TestpublicvoidguavaRetryTest03(){Retryer<Integer>retryer=RetryerBuilder.<Integer>newBuilder().withRetryListener(newCustomRetryerListener()).build();}staticclassCustomRetryerListenerimplementsRetryListener{Loggerlogger=LoggerFactory.getLogger(ReteyerBuidlerDemo.class);@Overridepublic<V>voidonRetry(Attempt<V>attempt){logger.warn(getClass()+"第"+attempt.getAttemptNumber()+"次重试");}}
案例总结
综合上面的三个维度,我们就可以根据需要来进行重试了,比如我们要进行数据库的异步插入,一旦这个地方发生了个异常数据就丢失了,这时候我们可以通过重试框架进行。
我们希望数据库插入的时候返回值<1的时候,重隔3s进行重试,超过1分钟就不再重试了,并且打印一下日志
//mockdataintretryCount=0;publicIntegermapperInsert(){returnretryCount;}publicvoidinsert()throwsExecutionException,RetryException{Retryer<Integer>retryer=RetryerBuilder.<Integer>newBuilder().retryIfResult(integer->integer==0).withStopStrategy(StopStrategies.stopAfterDelay(30,TimeUnit.SECONDS)).withWaitStrategy(WaitStrategies.fixedWait(5,TimeUnit.SECONDS)).withRetryListener(newCustomRetryerListener()).build();retryer.call(()->mapperInsert());}staticclassCustomRetryerListenerimplementsRetryListener{Loggerlogger=LoggerFactory.getLogger(ReteyerBuidlerDemo.class);@Overridepublic<V>voidonRetry(Attempt<V>attempt){logger.warn(getClass()+"第"+attempt.getAttemptNumber()+"次重试");}}
总结
以上就是这个guava-retrying组件库的使用方式了,通过建造者模式的retryer以达到消除if、for语句的效果。实现方式特别的简单。而且可以根据维度和策略来实现不同的效果。