扒一扒@Retryable注解,很优雅,有点意思! (3)

简介: 扒一扒@Retryable注解,很优雅,有点意思! (3)

image.png

一眼就能看出来了,这里面就是应该非常熟悉的动态代理机制,这里的 invocation 就是我们的 callChannel 方法:


image.png

从代码我们知道,callChannel 方法抛出的异常,在 doWithRetry 方法里面会进行捕获,然后直接扔出去:

image.png

这里其实也很好理解的,因为需要抛出异常来触发下一次的重试。

但是这里也暴露了一个 Spring-retry 的弊端,就是必须要通过抛出异常的方式来触发相关业务。

听着好像也是没有毛病,但是你想想一下,假设渠道方说如果我给你返回一个 500 的 ErrorCode,那么你也可以进行重试。

这样的业务场景应该也是比较多的。

如果你要用 Spring-retry 会怎么做?

是不是得写出这样的代码:

if(errorCode==500){
    throw new Exception("手动抛出异常");
}

意思就是通过抛出异常的方式来触发重试逻辑,算是一个不是特别优雅的设计吧。

其实根据返回对象中的某个属性来判断是否需要重试对于这个框架来说扩展起来也不算很难的事情。

你想,它这里本来就能拿到返回。只需要提供一个配置的入口,让我们告诉它当哪个对象的哪个字段为某个值的时候也应该进行重试。

当然了,大佬肯定有自己的想法,我这里都是一些不成熟的拙见而已。其实另外的一个重试框架 Guava-Retry,它就支持根据返回值进行重试。

不是本文重点就不扩展了。

接着往下看 while 循环中捕获异常的部分。

里面的逻辑也不复杂,但是下面框起来的部分可以注意一下:

image.png

这里又判断了一次是否可以重试,是干啥呢?

是为了执行这行代码:

backOffPolicy.backOff(backOffContext);

它是干啥的?

我也不知道,debug 看一眼,最后会走到这个地方:

org.springframework.retry.backoff.ThreadWaitSleeper#sleep


image.png

在这里执行睡眠 1000ms 的操作。

我一下就懂了,这玩意在这里给你留了个抓手,你可以设置重试间隔时间的抓手。然后默认给你赋能 1000ms 后重试的功能。


image.png

然后我在 @Retryable 注解里面找到了这个东西:

image.png

这玩意一眼看不懂是怎么配置的,但是它上面的注解叫我看看 Backoff 这个玩意。

它长这样:

image.png

这东西看起来就好理解多了,先不管其他的参数吧,至少我看到了 value 的默认值是 1000。

我怀疑就是这个参数控制的指定重试间隔,所以我试了一下:

image.png

果然是你小子,又让我挖到一个彩蛋。

在 @Backoff 里面,除了 value 参数,还有很多其他的参数,他们的含义分别是这样的:

  • delay:重试之间的等待时间(以毫秒为单位)
  • maxDelay:重试之间的最大等待时间(以毫秒为单位)
  • multiplier:指定延迟的倍数
  • delayExpression:重试之间的等待时间表达式
  • maxDelayExpression:重试之间的最大等待时间表达式
  • multiplierExpression:指定延迟的倍数表达式
  • random:随机指定延迟时间

就不一一给你演示了,有兴趣自己玩去吧。

因为丰富的重试时间配置策略,所以也根据不同的策略写了不同的实现:

image.png

通过 Debug 我知道了默认的实现是 FixedBackOffPolicy。

其他的实现就不去细研究了,我主要是抓主要链路,先把整个流程打通,之后自己玩的时候再去看这些枝干的部分。

在 Demo 的场景下,等待一秒钟之后再次发起重试,就又会再次走一遍 while 循环,重试的主链路就这样梳理清楚了。

其实我把代码折叠一下,你可以看到就是在 while 循环里面套了一个 try-catch 代码块而已:

image.png

这和我们之前写的丑代码的骨架是一样的,只是 Spring-retry 把这部分代码进行扩充并且藏起来了,只给你提供一个注解。

当你只拿到这个注解的时候,你把它当做一个黑盒用的时候会惊呼:这玩意真牛啊。

但是现在当你抽丝剥茧的翻一下源码之后,你就会说:就这?不过如此,我觉得也能写出来啊。

image.png

到这里前面抛出的问题中的前两个已经比较清晰了:

问题一:找到它的 for 循环在哪里。

没有 for 循环,但是有个 while 循环,其中有一个 try-catch。

问题二:它是怎么判断应该要重试的?

判断要触发重试机制的逻辑还是非常简单的,就是通过抛出异常的方式触发。

但是真的要不要执行重试,才是一个需要仔细分析的重点。

Spring-retry 有非常多的重试策略,默认是 SimpleRetryPolicy,重试次数为 3 次。

但是需要特别注意的是它这个“3次”是总调用次数为三次。而不是第一次调用失败后再调用三次,这样就共计 4 次了。关于到底调用几次的问题,还是得分清楚才行。

而且也不一定是抛出了异常就肯定会重试,因为 Spring-retry 是支持对指定异常进行处理或者不处理的。

可配置化,这是一个组件应该具备的基础能力。

还是剩下最后一个问题:它是怎么执行到 @Recover 逻辑的?

接着怼源码吧。

目录
相关文章
|
4月前
|
Java 编译器
你说啥什么?注解你还不会?
你说啥什么?注解你还不会?
34 0
|
4月前
|
安全 前端开发 Java
注解的使用
注解的使用
38 0
|
10月前
|
Java 编译器 数据库连接
注解
注解是JAVA5引入JAVA的一个特性,理解起来会有点抽象,这里笔者先给出自己对注解的一个理解——注解就是一张便签! 其次要有一个概念就是注解的应用是基于反射的。 本文举出的三个实例中例1和例3是引用其它的优秀文献 出处为how2J以及 https://blog.csdn.net/briblue/article/details/73824058一文
43 0
|
Java 测试技术 Spring
关于@RunWith注解的一点问题
IDEA写springboot测试关于@Runwith的小问题
142 0
关于@RunWith注解的一点问题
|
Java 编译器 Spring
什么是注解
什么是注解
|
Java 编译器
关于@FunctionalInterface注解
FunctionalInterface
320 0
关于@FunctionalInterface注解
|
存储 JSON Java
一文学会注解的正确使用姿势
一文学会注解的正确使用姿势
一文学会注解的正确使用姿势
扒一扒@Retryable注解,很优雅,有点意思! (4)
扒一扒@Retryable注解,很优雅,有点意思! (4)
188 0
扒一扒@Retryable注解,很优雅,有点意思! (4)
|
Java 程序员 开发工具
扒一扒@Retryable注解,很优雅,有点意思! (1)
扒一扒@Retryable注解,很优雅,有点意思! (1)
322 0
扒一扒@Retryable注解,很优雅,有点意思! (1)
|
缓存
扒一扒@Retryable注解,很优雅,有点意思! (2)
扒一扒@Retryable注解,很优雅,有点意思! (2)
250 0
扒一扒@Retryable注解,很优雅,有点意思! (2)

热门文章

最新文章