对于开发过网络应用程序的程序员来讲,重试并不陌生,因为网络的拥堵和波动,此刻不能访问服务的请求,也许过一小段时间就能够正常访问了。好比下面这段给某个手机号发SMS的伪代码: git
// 发送SMS 程序员
public boolean sendSMS(String phone, String content) github
{ spring
int retryTimes = 3; 网络
for(int i=0; i<=3; i++) ide
{ 工具
try ui
{ 开发
boolean result = doSomething(phone, content); it
// 发送成功直接返回
if(result == true)
{
return true;
}
}
catch(IOException e)
{
// 多是网络问题致使IOException,因此咱们继续重试
logger.error("send sms error", e);
}
catch(Exception e)
{
// 未知异常,与网络无关,有多是代码出现问题,这个时候重试没用,咱们直接返回false logger.error("unknown exception", e); return false;
}
}
return false;
}
// 给某人发短信
private boolean doSomething(String phone, String content)
{
}
这段代码有什么问题呢?看起来很丑,为了实现重试逻辑,各类if-else,各类try-catch。重试逻辑太简单,只是控制了重试次数,并无控制2次重试之间的时间间隔。由于重试代码与业务代码耦合在一块儿,因此看起来很复杂。
试想若是咱们要改变重试逻辑:好比咱们但愿每次重试事后,随机等待一段时间后再重试;好比咱们但愿重试次数不超过10次,并且总共的重试时间不超过1分钟;好比咱们但愿每次重试的时候,都给咱们监控系统发一条消息...随着重试逻辑的不断变化,上面代码会愈来愈复杂。并且重试逻辑,实际上是各个模块是差异不大的。
最近遇到2个开源项目,都是将重试代码封装成专门的工具,方便使用,好比guava-retrying和spring-retry。后面的文章,会介绍下如何使用guava-retrying。下面这段代码使用的是guava-retrying,明显能够感到代码变简单了。
public boolean sendSMS(final String phone, final String content)
{
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfResult(Predicates.equalTo(false)) // 返回false时重试 .retryIfExceptionOfType(IOException.class) // 抛出IOException时重试 .withWaitStrategy(WaitStrategies.fixedWait(200, TimeUnit.MILLISECONDS)) // 200ms后重试
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后中止
.build();
try {
return retryer.call(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return doSomething(phone, content);
}
});
} catch (Exception e) {
return false;
}
}
这2个项目github地址是:
https://github.com/rholder/guava-retrying
https://github.com/spring-projects/spring-retry guava-retrying
博文以下:
guava-retrying重试工具库: 何时重试
guava-retrying重试工具库: 何时终止
guava-retrying重试工具库: 隔多长时间重试
guava-retrying重试工具库: 阻塞策略BlockStrategy
guava-retrying重试工具库: AttemptTimeLimiter
guava-retrying重试工具库: RetryListener guava-retrying重试工具库:
Retryer.call()使用注意事项