还在手写重试,不妨试试Spring Retry(二)


theme: scrolls-light

🤞 个人主页:@青Cheng序员石头
🤞 粉丝福利:加粉丝群 一对一问题解答,获取免费的丰富简历模板、提高学习资料等,做好新时代的卷卷王!

紧接着上一篇《还在手写重试,不妨试试Spring Retry(一)》,点击前往。

四、RetryTemplate

RetryTemplate是使用注解形式重试的一种替代。

4.1 RetryOperations

使用 RetryOperations 接口的 Spring retry providesRetryOperations 策略。

public interface RetryOperations { ​ <T> T execute(RetryCallback < T > retryCallback) throws Exception; // other execute methods ​ <T> T execute(RetryCallback < T > retryCallback, RecoveryCallback < T > recoveryCallback,  RetryState retryState) throws Exception; }

RetryCallback允许插入在失败时需要重试的业务逻辑。

public interface RetryCallback <T> { T doWithRetry(RetryContext context) throws Throwable; }

4.2 RetryTemplate

RetryTemplate提供了RetryOperations的一种具体实现。它被认为是从中创建bean的良好做法。

   @Bean    @ConditionalOnMissingBean    public RetryTemplate retryTemplate(){        final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();        simpleRetryPolicy.setMaxAttempts(4); ​        final FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();        fixedBackOffPolicy.setBackOffPeriod(1000L); ​        return RetryTemplate.builder()           .customPolicy(simpleRetryPolicy)           .customBackoff(fixedBackOffPolicy)           .retryOn(CustomRetryException.class)           .build();   }

进行单元测试。

       @Autowired    pivate RetryTemplate retryTemplate; ​ @Test    void retryWithoutAnnotation(){        try {            String message = retryTemplate.execute(x -> retryService.retryWithoutAnnotation());            log.info("message = "+message);       } catch (CustomRetryException e) {            log.error("Error while executing test {}",e.getMessage());       }   }

如下图的输出结果所示,也是重试了四次,但是没有recover的策略。

fc44465528aeaf661622a9712a2aa3e.png

4.2 RecoveryCallback

execute时,可以选择输入RecoveryCallback回调,确定重试结束后,仍然出现异常的recovery行为。方法签名如下。

   public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback) throws E {        return this.doExecute(retryCallback, recoveryCallback, (RetryState)null);   }

所以,我们先自定义RecoveryCallback

@Slf4j public class CustomRecoveryCallback implements RecoveryCallback<String> { ​    @Override    public String recover(RetryContext retryContext) throws Exception {        log.info("Default Retry service test,total retry {}",retryContext.getRetryCount());        return "Error Class :: " + retryContext.getLastThrowable().getClass().getName();   } }

然后进行单元测试。

   @Autowired    private CustomRecoveryCallback customRecoveryCallback; ​ @Test    void retryWithoutAnnotation(){        try {            String message = retryTemplate.execute(x -> retryService.retryWithoutAnnotation(), customRecoveryCallback);            log.info("message = "+message);       } catch (CustomRetryException e) {            log.error("Error while executing test {}",e.getMessage());       }   }

如下图的输出结果所示,重试完成后,执行了我们自定义的recover

d5bfb79bc99998c1ba6ac3679df6c92.png

五、RetryListenerSupport

如果我们想在重试整个生命周期中,按照不同的阶段设置一些事件监听处理机制,那怎么办呢?设置自定义的RetryListenerSupport能帮助到我们。我们继承RetryListenerSupport,并重新Override closeonErroropen方法,这三个方法分别表示

  • 所有重试结束时
  • 每一次重试发生异常时
  • 重试正式开始前。

@Slf4j public class DefaultListenerSupport extends RetryListenerSupport { ​    @Override    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {        log.info("DefaultListenerSupport close");        super.close(context, callback, throwable);   } ​    @Override    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {        log.info("DefaultListenerSupport onError");        super.onError(context, callback, throwable);   } ​    @Override    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {        log.info("DefaultListenerSupport open");        return super.open(context, callback);   } }

并且在构造RetryTemaplate时候,设置withListener字段。

``` @Bean @ConditionalOnMissingBean public RetryListenerSupport retryListenerSupport(){ return new DefaultListenerSupport(); } ​ @Bean @ConditionalOnMissingBean public RetryTemplate retryTemplate(RetryListenerSupport retryListenerSupport){ ​ ...

  return RetryTemplate.builder()       .customPolicy(simpleRetryPolicy)       .customBackoff(fixedBackOffPolicy)       .withListener(retryListenerSupport)       .retryOn(CustomRetryException.class)       .build(); } ```

运行单元测试,输出结果如下。

908cfaf861fd507821e7005ca3214e5.png

六、总结

在这篇文章中,我们看到了 Spring retry的不同特性,我们清楚使用它能使应用程序更加健壮。我们实践了Spring Retry最为常见的几种用法,主要包括@Retryable 注释和 RetryTemplate

那么哪些地方我们能用到Spring Retry呢?有这两点建议

  • 仅在临时错误上使用重试。不建议它在永久错误中使用它,因为这样可能导致系统性能问题。
  • 它不是熔断器的替代的一种方式,最好在允许的情况下,既使用熔断器,又使用重试器。

少年,没看够?点击石头的详情介绍,随便点点看看,说不定有惊喜呢?欢迎支持点赞/关注/评论,有你们的支持是我更文最大的动力,多谢啦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值