使用背景
需要重复尝试执行某些动作,guava-retrying 提供了成型的重试框架
依赖
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>${retrying.version}</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
${retrying.version} 实际为 2.0.0
如果已有 guava 缓存框架引入则可以像上面一样去除,否则可以保留
定义工具类
import com.github.rholder.retry.Attempt;
import com.github.rholder.retry.RetryListener;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.StopStrategy;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.base.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.StopWatch;
import java.text.MessageFormat;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@Slf4j
public class RetryUtil {
private RetryUtil(){}
/**
* 根据输入的condition重复做task,在规定的次数内达到condition则返回,
* 如果超过retryTimes则返回null, 重试次数,整个重试时间以及retry exception都会记录log
* 需要注意调用的时候需要新起一个线程,不然重试失败会阻塞当前线程
*
* @param condition 重试条件,比如接口返回errorCode为处理中,或不是最终需要的结果
* @param task 重试做的任务
* @param sleepTime 重试间隔时间,单位毫秒
* @param retryTimes 最大重试次数
* @return targetBean
*/
public static <V> Optional<V> retry(Predicate<V> condition, Callable<V> task, int sleepTime, Integer retryTimes) {
Optional<V> result = Optional.empty();
StopStrategy stopStrategy = null;
if (retryTimes == null) {
stopStrategy = StopStrategies.neverStop();
} else {
stopStrategy = StopStrategies.stopAfterAttempt(retryTimes);
}
StopWatch stopWatch = new StopWatch();
try {
stopWatch.start();
Retryer<V> retry = RetryerBuilder.<V>newBuilder()
// 默认任务执行过程中发生异常自动重试
.retryIfException()
// 重试条件(按照业务场景)
.retryIfResult(condition)
// 等待策略
.withWaitStrategy(WaitStrategies.fixedWait(sleepTime, TimeUnit.MILLISECONDS))
// 重试策略
.withStopStrategy(stopStrategy)
// 重试监听器
.withRetryListener(new RetryListener() {
@Override
public <V> void onRetry(Attempt<V> attempt) {
// 记录重试次数和异常信息
log.info(MessageFormat.format("{0}th retry", attempt.getAttemptNumber()));
if (attempt.hasException()) {
log.error(MessageFormat.format("retry exception:{0}", attempt.getExceptionCause()));
}
}
}).build();
// 开始执行重试任务
result = Optional.ofNullable(retry.call(task));
} catch (Exception e) {
log.error("retry fail:", e);
} finally {
stopWatch.stop();
log.info("retry execute time" + stopWatch.getTime());
}
return result;
}
}
实际使用
private void xxxxXxxxx() {
Predicate<Boolean> condition = result -> result;
RetryUtil.retry(condition::test,
() -> xxxxxxService.xxxxXxxx(),
60000,
60);
}
其中 xxxxxxService.xxxxXxxx() : 方法为要重试的内容,同时改方法要返回
条件的指定类型。
第一个为入参为重试条件。
60000 代表重试时间间隔,单位 毫秒
60 标识重试次数