如何设计一个 retry 的 java 库

retry 这样简单的 logic 在不少 engineering 里面都会存在,或者因为调用外部的 service 或者因为数据库访问中某些 exception 是 expected 的(如 race condition),简单的做法就是写个 for 循环,里面去 try/catch/finally,这样的 pattern 破坏了 business logic,比较好的做法自然是以一种 non-intrusive 的策略实现。

retry 的问题是什么

一般来说 retry 是因为碰到了某些 recoverable 的 exception,我们需要将抛出的异常的类别进行判别,如果是 recoverable 的我们就可以使用重试。重试莫过于这样几种

  • 给定重试次数、重试间隔
  • 是否使用 jittering,和 jittering 的程度

这可以抽象成为一个简单的 interface,一个函数判断给定 Throwable 是否 recoverable,一个函数返回两次重试之间的间隔时间,基于此我们可以实现不同的 policy。下面的事情就是如何把这些东西添加到现有的类/方法上。

callable 接口与 proxy

通常一个调用常利用 Callable 接口来 wrap,我们这里将 retry 的 logic 可以封装在对这个 Callable 接口的实现类里面,这个类通过 policy 判断,实现 retry。为了方便创建这样一个类,我们可以使用 proxy,Java 的 proxy 相当于在原有对象前面放置一个屏障,所有的调用都会经由这个屏障处理后决定是否递交给原对象,这类似 decorator 的 pattern,因为 retry 是简单的 forward 调用即可,我们可以在这里创建我们的 Callable 实现类,并调用 call() 返回结果,当然如何判断哪些方法是需要被 wrap 的就成了下一个问题。

annotation

我们知道 annotation 可以作用在方法上,因此通过 annotation 表达 retry 的 logic 需要的参数是一个明智的选择,这样 decorator 就能在需要的方法(被 annotate 过的)构造需要的 policy 和 Callable 的实现。使用这个库,有两个麻烦的地方

  • annotation 的 import,这个可能还好
  • decorator 的使用,看起来有点破坏了原有 code 的逻辑

我们是否能去掉后者?

AspectJ 与 SpringAOP

AOP 前面已经谈过了,这类应用是 AOP 解决的典型用例,直接使用 AspectJ 编译获得的需要用 weaver 混入现有代码,而 Spring 是通过 XML 文件定义 pinpoint 和使用的 aspect 各有优缺点,如果前者不需要额外的 build tools 的支持就更完美了。

——————
And thither were all the flocks gathered: and they rolled the stone from the well’s mouth, and watered the sheep, and put the stone again upon the well’s mouth in his place.

Advertisements
如何设计一个 retry 的 java 库

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s