java8 retry 重试机制_Spring Boot教程(27) – 优雅地实现重试逻辑

54ccbf842e18ef2435b78b559c6b304d.png

Spring家族中有一个小项目是Spring Retry,他用来方便地实现重试机制。你的程序在调用远程API的时候,可能因为网络抖动等原因,导致调用失败,这种失败是偶发的,出现了多试几下就行。如果你遇到需要重试多次的情况,倘若只是朴素地写个for循环N次,请求成功就跳出循环返回结果,未免也太粗糙了。Spring已经为这一通用需求造好了轮子。

Spring Retry现在被Spring Batch和Spring Integration等项目引用,在Spring Boot项目中,我一般直接引入spring-boot-start-batch来间接引入Spring Retry(Spring Batch是个批处理工具库,你也许会用得到其中的类,我们有机会再具体研究下),另外它还要配合AOP Starter才能使用,后者你的项目里一般都有。引入Spring Retry后,可以用加注解声明的方式,或者编码的方式来使用。

先来看看最简单的使用注解:

9e6306c2981833d8bde57f2d96999a27.png
开启功能

4967c788560fc06e88466c95e0be5563.png
编写业务逻辑

Spring Retry使用了Spring AOP,一旦通过@EnableRetry开启重试的功能,那么@Retryable注解对应的方法就会被包装起来。就像上图中的yourMethod方法,如果执行的过程中发生了异常,那么yourMethod就会再次执行,直到次数到了注解参数maxAttempts指定的最大值,如果所有尝试都不成功,异常就会抛出。@Retryable注解还有很多其他参数,比如include参数可以指定发生哪些异常会重试,比如exclude参数表示哪些异常不重试,还有backoff参数可以指定一次失败之后,过多长时间再开始下一次重试。

由于Spring AOP的缺陷,你不能在上图中DemoService的内部调用yourMethod方法,否则会失去重试的效果。你只能在DemoService外部调用,或者使用比较tricky的方法来绕过。具体原因可以查看我之前写的讲解AOP的文章。

注解声明式的使用,最终执行的时候也是使用了一套基本的编程API,我们简单看一下如何直接使用这些API:

3725553d70539fdab451c7d97bba9bcd.png

上图的代码中,我们创建了一个RetryTemplate对象,这个对象是重试相关操作的发起者。接下来设置了他的RetryPolicyBackOffPolicy,这两个接口,前者定义了什么情况下开始重试,什么情况下停止重试,后者定义了两次重试的时间间隔应该如何确定。

RetryTemplate中默认的RetryPolicySimpleRetryPolicy,它限制尝试的次数,最大尝试次数是3。如果你想自定义,可以像上图那样自己创建一个SimpleRetryPolicy对象,用它设置你想要的最大重试次数,和遇到哪些异常才重试。我建议你显式指定需要重试的异常,比如IO异常,这样可以避免某些情况下进行无谓的重试,浪费时间。

RetryTemplate中默认的BackOffPolicyNoBackOffPolicy,意思就是一旦失败了,就立马重试。如果你觉的需要定义间隔时间,可以使用如下几个:

  • FixedBackOffPolicy 设定固定的间隔时间
  • UniformRandomBackOffPolicy 在一个时间区间内[min,max]随机出一个时间间隔
  • ExponentialBackOffPolicy 让间隔时间按照某种比例增长,比如第一次隔1秒,第二次隔2秒,第三次隔4秒,依此类推。setInitialInterval方法设置初始的间隔时间,setMultiplier设置时间的增长速率,默认是2,意味着每次时间翻倍。你也可以给间隔时间设置一个上限,时间过长

说完了RetryPolicyBackOffPolicy,我们再来看看RetryTemplate的核心方法execute,他的参数是一个RetryCallback对象,其中doWithRetry方法里运行的就是你的业务逻辑,你可以将单独的业务逻辑提取出来,在doWithRetry中调用。代码如下,businessLogic方法独立出来后可以在其他地方复用,execute方法调用显得更简洁,且返回值result就是businessLogic方法执行成功时候的返回值:

d12a312678872caea6cbfdf75207f353.png

doWithRetry方法有个RetryContext类型的参数context,它包含了此次重试的信息,比如当前是第几次重试,上次发生的异常等等。一般情况下你不会用到它。

RetryTemplate的execute方法还有另外一种重载形式,多了一个RecoveryCallback对象。它是在重试次数用完的时候调用的,默认情况下用完次数会抛出异常,但是你可能懒得去处理,可以用RecoveryCallback,在所有重试都失败的时候提供个返回值,可以是默认值或者是从其他什么地方拿来的值。

不管是使用注解声明还是使用RetryTemplate,他们的效果其实等价的,后者可能更灵活一点,我经常在项目中选用。如果你想看注解是如何被转换成RetryTemplate调用的,可以查看AnnotationAwareRetryOperationsInterceptor类。

Spring Retry的内容还是挺简单的,Spring生态中有很多这种使常见代码模型简化的工具,如果你都掌握的很好,可以使你的代码显得更健壮更易读。

博客链接:https://fookwood.com/spring-boot-tutorial-27-retry

568915f3f8bf2cdbc9bc438b4921be5b.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值