除了使用注解外,Spring Retry 也支持直接在调用时使用代码进行重试:
@Test public void normalSpringRetry() { // 表示哪些异常需要重试,key表示异常的字节码,value为true表示需要重试 Map<Class<? extends Throwable>, Boolean> exceptionMap = new HashMap<>(); exceptionMap.put(HelloRetryException.class, true); // 构建重试模板实例 RetryTemplate retryTemplate = new RetryTemplate(); // 设置重试回退操作策略,主要设置重试间隔时间 FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); long fixedPeriodTime = 1000L; backOffPolicy.setBackOffPeriod(fixedPeriodTime); // 设置重试策略,主要设置重试次数 int maxRetryTimes = 3; SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(maxRetryTimes, exceptionMap); retryTemplate.setRetryPolicy(retryPolicy); retryTemplate.setBackOffPolicy(backOffPolicy); Boolean execute = retryTemplate.execute( //RetryCallback retryContext -> { String hello = helloService.hello(); log.info("调用的结果:{}", hello); return true; }, // RecoverCallBack retryContext -> { //RecoveryCallback log.info("已达到最大重试次数"); return false; } ); }此时唯一的好处是可以设置多种重试策略:
NeverRetryPolicy:只允许调用RetryCallback一次,不允许重试 AlwaysRetryPolicy:允许无限重试,直到成功,此方式逻辑不当会导致死循环 SimpleRetryPolicy:固定次数重试策略,默认重试最大次数为3次,RetryTemplate默认使用的策略 TimeoutRetryPolicy:超时时间重试策略,默认超时时间为1秒,在指定的超时时间内允许重试 ExceptionClassifierRetryPolicy:设置不同异常的重试策略,类似组合重试策略,区别在于这里只区分不同异常的重试 CircuitBreakerRetryPolicy:有熔断功能的重试策略,需设置3个参数openTimeout、resetTimeout和delegate CompositeRetryPolicy:组合重试策略,有两种组合方式,乐观组合重试策略是指只要有一个策略允许即可以重试, 悲观组合重试策略是指只要有一个策略不允许即可以重试,但不管哪种组合方式,组合中的每一个策略都会执行可以看出,Spring中的重试机制还是相当完善的,比上面自己写的AOP切面功能更加强大。
这里还需要再提醒的一点是,由于Spring Retry用到了Aspect增强,所以就会有使用Aspect不可避免的坑——方法内部调用,如果被 @Retryable 注解的方法的调用方和被调用方处于同一个类中,那么重试将会失效。
但也还是存在一定的不足,Spring的重试机制只支持对异常进行捕获,而无法对返回值进行校验。
Guava Retry最后,再介绍另一个重试利器——Guava Retry。
相比Spring Retry,Guava Retry具有更强的灵活性,可以根据返回值校验来判断是否需要进行重试。
先来看一个小栗子:
先引入jar包:
<dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency>然后用一个小Demo来感受一下:
@Test public void guavaRetry() { Retryer<String> retryer = RetryerBuilder.<String>newBuilder() .retryIfExceptionOfType(HelloRetryException.class) .retryIfResult(StringUtils::isEmpty) .withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS)) .withStopStrategy(StopStrategies.stopAfterAttempt(3)) .build(); try { retryer.call(() -> helloService.hello()); } catch (Exception e){ e.printStackTrace(); } }先创建一个Retryer实例,然后使用这个实例对需要重试的方法进行调用,可以通过很多方法来设置重试机制,比如使用retryIfException来对所有异常进行重试,使用retryIfExceptionOfType方法来设置对指定异常进行重试,使用retryIfResult来对不符合预期的返回结果进行重试,使用retryIfRuntimeException方法来对所有RuntimeException进行重试。
还有五个以with开头的方法,用来对重试策略/等待策略/阻塞策略/单次任务执行时间限制/自定义监听器进行设置,以实现更加强大的异常处理。
通过跟Spring AOP的结合,可以实现比Spring Retry更加强大的重试功能。
仔细对比之下,Guava Retry可以提供的特性有:
可以设置任务单次执行的时间限制,如果超时则抛出异常。
可以设置重试监听器,用来执行额外的处理工作。
可以设置任务阻塞策略,即可以设置当前重试完成,下次重试开始前的这段时间做什么事情。
可以通过停止重试策略和等待策略结合使用来设置更加灵活的策略,比如指数等待时长并最多10次调用,随机等待时长并永不停止等等。
总结