3年前我在中通快递的时候,写过一个重试的功能。今天看了up主我是秦同学写的轮子,感觉有异曲同工之妙,在此做个总结
1. 利用模板方法写一个重试功能
/**
* 锁重试工具类
*/
@Slf4j
public abstract class TemplateRetryingUtil<T> {
/**
* 指定重试次数,必须重写
* @return
*/
protected abstract int retryTimes();
protected abstract Long waitTime();
/**
* 重试业务逻辑,需要用户自己实现
*/
protected abstract T getDataByRetrying(Object... param);
public T retry(Object... param) {
// 获取重试次数
int retryTimes = retryTimes();
// 重试次数条件限制
if (retryTimes <= 0){
log.warn("重试次数必须大于0");
throw new RuntimeException("重试次数必须大于0");
}
// 辅助判断是否重试成功
boolean retryIsSuccess = Boolean.FALSE;
T result = null;
// 开始重试
for (int cnt = 0; cnt < retryTimes; cnt++) {
try {
//TODO 测试代码 , 可以扔掉的
if (cnt == 0){
throw new RuntimeException("测试异常");
}
// 调用重试逻辑
result = this.getDataByRetrying(param);
//设置重试成功的flag
retryIsSuccess = true;
break;
} catch (Exception e) {
log.info("第:{}次重试失败,方法参数是:{},异常信息是:{}",cnt + 1,param,e.getMessage());
try {
Thread.sleep(waitTime());
} catch (InterruptedException interruptedexception) {
log.warn("线程等待异常,异常信息是:{}", interruptedexception.getMessage());
}
}
}
// 查询正常
if (retryIsSuccess) {
return result;
}
log.info("重试失败");
return null;
}
}
public class TemplateRetryingUtilTest extends TemplateRetryingUtil<String> {
public static void main(String[] args) {
TemplateRetryingUtilTest TemplateRetryingUtilTest = new TemplateRetryingUtilTest();
Object retry = TemplateRetryingUtilTest.retry();
System.out.println("重试获得的结果为:"+retry);
System.out.println("============================");
TemplateRetryingUtilTest templateRetryingUtilTest1 = new TemplateRetryingUtilTest();
Object retry1 = TemplateRetryingUtilTest.retry("a","b");
System.out.println("重试获得的结果为:"+retry1);
}
@Override
protected int retryTimes() {
return 4;
}
@Override
protected Long waitTime() {
return 1000L;
}
@Override
protected String getDataByRetrying(Object... param) {
System.out.println("重试代码中.....");
System.out.println("重试人工号"+new Random().nextInt(10));
return "重试成功";
}
}
利用lambda表达式回调重试,本质上也是模板方法
/**
*定义重试任务的返回结果类型是什么
*/
public class LambdaRetryUtil<R> {
//重试等待时间长度
private long waitTime = 500L;
//重试次数
private int retryTimes = 3;
// 返回结果
private R result;
// 需要重试的业务逻辑
private Supplier<R> supplier;
// 重试业务入口处
public static <T> LambdaRetryUtil<T> fromTask(Supplier<T> supplier) {
// 重试任务实力定义
LambdaRetryUtil<T> retryUtil = new LambdaRetryUtil<>();
retryUtil.setSupplier(supplier);
return retryUtil;
}
public LambdaRetryUtil<R> setSupplier(Supplier<R> supplier) {
this.supplier = supplier;
return this;
}
public LambdaRetryUtil<R> retryTimes(int retryTimes) {
this.retryTimes = retryTimes;
return this;
}
public LambdaRetryUtil<R> retryWaitTime(long waitTime) {
this.waitTime = waitTime;
return this;
}
// 重试业务逻辑
public Optional<R> getResult() {
int curRetryTime = 1;
// 辅助判断是否重试成功
boolean retryIsSuccess = Boolean.FALSE;
while (curRetryTime < retryTimes + 1) {
try {
// 执行逻辑
result = this.supplier.get();
if (curRetryTime <= 1){
curRetryTime++;
throw new RuntimeException("运行过程中发生了异常");
}
curRetryTime++;
retryIsSuccess = Boolean.TRUE;
} catch (Exception e) {
if (curRetryTime < this.retryTimes) {
// 进行下一轮重试
try {
Thread.sleep(this.waitTime);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
continue;
}
// 异常且无需重试
throw new RuntimeException(e);
}
// 检查是否运行成功
if (retryIsSuccess) {
// 运行成功,返回结果
return (Optional<R>) Optional.ofNullable(result);
}
}
// 运行到这里说明一次都没有成功
return Optional.empty();
}
}
public class LambdaRetryUtilTest {
public static void main(String[] args) {
Optional<Boolean> result = LambdaRetryUtil.<Boolean>fromTask(() -> {
return task();})
.retryTimes(3)
.retryWaitTime(500L)
.getResult();
Optional<Boolean> result1 = LambdaRetryUtil.<Boolean>fromTask(() -> {
return task();})
.retryTimes(3)
.retryWaitTime(500L)
.getResult();
}
public static Boolean task(){
System.out.println("测试");
return Boolean.TRUE;
}
}
guava retry
/**
* @Author: dingyawu
* @Description: TODO
* @Date: Created in 16:42 2022/11/27
*/
public class TestGuavaRetry {
/**
*
*/
private static Retryer<Integer> retryer = RetryerBuilder.<Integer>newBuilder()
.retryIfException()
.withStopStrategy(StopStrategies.stopAfterAttempt(3))
.withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))
.build();
@Test
public void testRetry(){
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return doQuery();
}
};
int result;
try {
result = retryer.call(callable);
} catch (Exception e) {
//多次充实不满足以后,就返回-1了
result = -1;
}
System.out.println(result);
}
private int doQuery() {
Random r = new Random(System.currentTimeMillis());
int num = r.nextInt(5);
System.out.println("query result " + num);
if (num == 0) {
return 0;
} else if (num == 1) {
System.out.println("DBException");
throw new RuntimeException("DBException");
} else if (num == 2) {
System.out.println("IllegalArgumentException");
throw new IllegalArgumentException("IllegalArgumentException");
} else if (num == 3) {
System.out.println("NullPointerException");
throw new NullPointerException("NullPointerException");
} else {
System.out.println("IndexOutOfBoundsException");
throw new IndexOutOfBoundsException("IndexOutOfBoundsException");
}
}
}