1、引言
业务开发中很可能与回到重试的场景。
重试主要在调用失败时重试,尤其是发生dubbo相关异常,网络相关异常的时候。
下面对该功能简单作封装,然后给出一些相对用的多一些的开源代码地址。
2、简单封装
github地址 https://github.com/chujianyun/simple-retry
可下载运行,可fork改进,欢迎提出宝贵意见,欢迎贡献代码。
封装操作
package com.chujianyun.simpleretry;
/**
* 操作接口方法
*
* @author: 明明如月 [email protected]
* @date: 2019-04-05 02:09
*/
public interface Operation<T> {
T execute() throws Exception;
}
封装重试
package com.chujianyun.simpleretry;
import com.chujianyun.simpleretry.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
/**
* 操作封装
*
* @author: 明明如月 [email protected]
* @date: 2019-04-05 02:09
*/
@Slf4j
public class OperationHelper {
/**
* 带重试的操作执行
*
* @param operation 执行的操作
* @param maxAttemptTimes 最大重试次数
* @param <T> 返回值类型
* @return 返回值
* @throws Exception
*/
public static <T> T executeWithRetry(Operation<T> operation, int maxAttemptTimes) throws Exception {
return executeWithRetry(operation, maxAttemptTimes, 0, null);
}
/**
* 带重试和延时的操作执行
*
* @param operation 执行的操作
* @param maxAttemptTimes 最大重试次数
* @param timeDelay 延时
* @param timeUnit 时间单位
* @param <T> 返回值类型
* @return 返回值
* @throws Exception
*/
public static <T> T executeWithRetry(Operation<T> operation, int maxAttemptTimes, int timeDelay, TimeUnit timeUnit) throws Exception {
if (maxAttemptTimes < 1) {
throw new IllegalArgumentException("max attempt times must not less than one");
}
int count = 1;
while (true) {
try {
return operation.execute();
} catch (BusinessException businessException) {
log.debug("OperationHelper#businessException", businessException);
throw new BusinessException();
} catch (Exception e) {
log.debug("OperationHelper#executeWithRetry", e);
//累计
count++;
if (count > maxAttemptTimes) {
throw e;
}
// 延时
if (timeDelay >= 0 && timeUnit != null) {
try {
log.debug("延时{}毫秒", timeUnit.toMillis(timeDelay));
timeUnit.sleep(timeDelay);
} catch (InterruptedException ex) {
//ignore
}
}
log.debug("第{}次重试", count - 1);
}
}
}
}
遇到业务异常就没必要重试了,直接扔出去。
当遇到非业务异常是,未超出最大重试次数时,不断重试,如果设置了延时则延时后重试。
测试类
package com.chujianyun.simpleretry;
import com.chujianyun.simpleretry.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* 重试测试
*
* @author: 明明如月 [email protected]
* @date: 2019-04-04 10:42
*/
@Slf4j
public class OperationHelperTest {
@Test(expected = Exception.class)
public void executeWithRetry_Exception() throws Exception {
OperationHelper.executeWithRetry(() -> {
throw new Exception();
}, 3, 5, TimeUnit.SECONDS);
}
@Test(expected = BusinessException.class)
public void executeWithRetry_BusinessException() throws Exception {
OperationHelper.executeWithRetry(() -> {
throw new BusinessException();
}, 3, 5, TimeUnit.SECONDS);
}
@Test
public void executeWithRetry() throws Exception {
Integer result = OperationHelper.executeWithRetry(() -> {
// 随机数为奇数时报参数异常,会重试
Random random = new Random();
int nextInt = random.nextInt(100);
if ((nextInt & 1) == 1) {
log.debug("生成的随机数{}为奇数,报错后会触发重试", nextInt);
throw new IllegalArgumentException();
}
return random.nextInt(5);
}, 3, 5, TimeUnit.SECONDS);
log.debug("最终返回值{}", result);
}
}