起因
有同事写上传,发现上传失败,然后调用别人上传接口还吃掉异常
特点
可以重试任何方法,可以回调,异常转移....
代码实现
/**
* 方法重试
* <p>
* Created by songzhaoying on 2020/11/19 17:12.
*
* @author songzhaoying@a.com.
* @date 2020/11/19 17:12.
*/
public class FunctionUtil {
private static final Logger logger = LoggerFactory.getLogger(FunctionUtil.class);
/**
* 重试间隔毫秒
*/
public static final long DEFAULT_SLEEP_MILLIS = 20;
/**
* 默认重试次数
* 实际执行 + 1
*/
public static final int DEFAULT_RETRY_COUNT = 0;
/**
* 一次重试
* 实际执行 + 1
*/
public static final int ONCE_RETRY_COUNT = 1;
/**
* 常规 重试次数
* 实际执行 + 1
*/
public static final int NORMAL_RETRY_COUNT = 2;
/**
* 常规 重试次数
* 实际执行 + 1
*/
public static final int TRIPLE_RETRY_COUNT = 3;
/**
* 参数转换字符串 异常提示
*/
protected static final String PARAM_TRAN_ERR = "参数特殊:不能转字符串";
/**
* 重试策略
* t -> t * DEFAULT_SLEEP_MILLIS
*/
protected static final IntFunction<Long> SLEEP_FUN = t -> DEFAULT_SLEEP_MILLIS;
/**
* 超长调用
*/
protected static final long BIG_TIME_LIMIT = TimeUnit.SECONDS.toMillis(6);
/**
* 超长调用次数
*/
public static final AtomicInteger BIG_TIME_COUNT = new AtomicInteger(0);
/**
* 隐式公有构造
*/
private FunctionUtil() {
}
/**
* 重试 R
*
* @param function 数据转换-功能函数
* @param param 功能函数-入参
* @param <P>
* @param <R>
* @return
* @throws Exception
*/
public static <P, R> R retryFunction(Function<? super P, R> function, P param) throws Exception {
return retryFunction(function, param, DEFAULT_RETRY_COUNT);
}
/**
* 重试 R
*
* @param function 数据转换-功能函数
* @param param 功能函数-入参
* @param tryTimes 重试次数
* @param <P>
* @param <R>
* @return
* @throws Exception
*/
public static <P, R> R retryFunction(Function<? super P, R> function
, P param
, final int tryTimes) throws Exception {
return retryFunction(function, param, tryTimes, null, null, null);
}
/**
* 重试 void
*
* @param consumer 数据转换-功能函数
* @param param 功能函数-入参
* @param <P>
* @return
* @throws Exception
*/
public static <P> void retryFunction(Consumer<? super P> consumer, P param) throws Exception {
retryFunction(consumer, param, DEFAULT_RETRY_COUNT, null, null);
}
/**
* 重试 void
*
* @param consumer 数据处理-消费函数
* @param param 消费函数-入参
* @param tryTimes 重试次数
* @param <P>
* @return
* @throws Exception
*/
public static <P> void retryFunction(Consumer<? super P> consumer
, P param
, final int tryTimes) throws Exception {
retryFunction(consumer, param, tryTimes, null, null);
}
/**
* 方法重试
*
* @param function 数据转换-功能函数
* @param param 功能函数-入参
* @param tryRunCount 重试次数,实际执行 + 1
* @param functionTag 重试失败日志 标记
* @param runExceptionSupplier 执行异常转移
* @param callBack 结果回调
* @param <P>
* @param <R>
* @return
* @throws Exception
*/
public static <P, R, R1> R1 retryFunction(Function<? super P, R> function
, P param
, final int tryRunCount
, final String functionTag
, Supplier<? extends RuntimeException> runExceptionSupplier
, Function<? super R, R1> callBack) throws Exception {
R ret = retry(function, param, tryRunCount, functionTag, runExceptionSupplier);
return handlerRetFunction(ret, callBack);
}
/**
* 重试 消费 无返回
*
* @param consumer 数据处理-消费函数
* @param param 消费函数-入参
* @param tryRunCount 重试次数
* @param functionTag 重试失败日志 标记
* @param runExceptionSupplier 执行异常转移
* @param <P>
* @return
* @throws Exception
*/
public static <P> void retryFunction(Consumer<? super P> consumer
, P param
, final int tryRunCount
, final String functionTag
, Supplier<? extends RuntimeException> runExceptionSupplier) throws Exception {
retry(consumer, param, tryRunCount, functionTag, runExceptionSupplier);
}
/**
* 重试 R
*
* @param function 数据转换-功能函数
* @param param 功能函数-入参
* @param tryRunCount 重试次数,实际执行 + 1
* @param functionTag 重试失败日志 标记
* @param runExceptionSupplier 执行异常转移,可以null
* @param <P>
* @param <R>
* @return
* @throws Exception
*/
private static <P, R> R retry(Function<? super P, R> function, P param
, int tryRunCount
, String functionTag
, Supplier<? extends RuntimeException> runExceptionSupplier) throws Exception {
R ret;
ret = getRetry(getFunPair(function, null), param, tryRunCount, functionTag, runExceptionSupplier);
return ret;
}
/**
* 重试 void
*
* @param consumer 数据处理-消费函数
* @param param 消费函数-入参
* @param tryRunCount 重试次数,实际执行 + 1
* @param functionTag 重试失败日志 标记
* @param runExceptionSupplier 执行异常转移,可以null
* @param <P>
* @throws Exception
*/
private static <P> void retry(Consumer<? super P> consumer
, P param
, int tryRunCount
, String functionTag
, Supplier<? extends RuntimeException> runExceptionSupplier) throws Exception {
getRetry(getFunPair(null, consumer), param, tryRunCount, functionTag, runExceptionSupplier);
return;
}
/**
* 生成 pair
*
* @param function 功能函数
* @param consumer 消费函数
* @param <P>
* @param <R>
* @return
*/
private static <P, R> Pair<Function<? super P, R>, Consumer<? super P>> getFunPair(Function<? super P, R> function
, Consumer<? super P> consumer) {
if (function != null) {
return ImmutablePair.of(function, null);
}
if (consumer != null) {
return ImmutablePair.of(null, consumer);
}
return null;
}
/**
* 返回重试结果
*
* @param functionPair 函数
* @param param 入参
* @param tryRunCount 重试次数
* @param functionTag 重试失败日志 标记
* @param runExceptionSupplier 执行异常转移
* @param <P>
* @param <R>
* @return
* @throws Exception
*/
private static <P, R> R getRetry(Pair<Function<? super P, R>, Consumer<? super P>> functionPair
, P param, int tryRunCount
, String functionTag
, Supplier<? extends RuntimeException> runExceptionSupplier) throws Exception {
if (functionPair == null) {
return null;
}
R ret = null;
// 开始计时
long startMillis = System.currentTimeMillis();
// 所有执行次数 重试次数 + 1
int allRunCount = Math.max(0, tryRunCount) + 1;
// 重试第几次 默认 = 1
int retryNum = 0;
for (int leftRunNum = allRunCount; leftRunNum > 0; leftRunNum--, retryNum++) {
try {
ret = getRet(functionPair, param);
break;
} catch (Exception e) {
handlerRunException(param, functionTag, runExceptionSupplier, leftRunNum, retryNum, e);
}
long sleepMillis = SLEEP_FUN.apply(retryNum);
Thread.sleep(sleepMillis);
}
// 结束计时
long callMillis = System.currentTimeMillis() - startMillis;
if (callMillis > BIG_TIME_LIMIT) {
BIG_TIME_COUNT.incrementAndGet();
logger.info("接口重试日志:[functionTag={}] Long call millis:{}, retryNum:{}"
, functionTag, callMillis, retryNum);
}
return ret;
}
/**
* 执行
*
* @param functionPair 函数
* @param param 参数
* @param <P>
* @param <R>
* @return
*/
private static <P, R> R getRet(Pair<Function<? super P, R>, Consumer<? super P>> functionPair, P param) {
// 如果是功能函数
if (functionPair.getLeft() != null) {
return functionPair.getLeft().apply(param);
}
functionPair.getRight().accept(param);
return null;
}
/**
* 处理 执行异常
*
* @param param 参数
* @param functionTag 重试失败日志 标记
* @param runExceptionSupplier 执行异常转移
* @param leftRunNum 剩余次数
* @param retryNum 重试次数
* @param e 异常
* @throws Exception
*/
private static <P> void handlerRunException(P param
, String functionTag
, Supplier<? extends RuntimeException> runExceptionSupplier
, int leftRunNum, int retryNum, Exception e) {
if (StringUtils.isNotBlank(functionTag)) {
if (leftRunNum > 1) {
logger.info("调用重试日志: [functionTag={}], CallCount:{}, Throw Exception: ", functionTag
, retryNum + 1, e);
return;
}
// 入参
String paramStr = PARAM_TRAN_ERR;
try {
paramStr = JsonUtil.writeValueAsString(param);
} catch (Exception e1) {
}
logger.info("调用异常日志:[functionTag={}], CallCount={}, Req=({}: {}), CallErr=", functionTag
, retryNum + 1, StrUtil.getMD5(paramStr), paramStr, e);
}
if (runExceptionSupplier != null && runExceptionSupplier.get() != null) {
throw runExceptionSupplier.get();
} else {
throw new RuntimeException(e);
}
}
/**
* 指定 返回
*
* @param ret 结果
* @param callBack 回调
* @param <R>
* @throws Exception
*/
private static <R, R1> R1 handlerRetFunction(R ret, Function<? super R, R1> callBack) {
if (callBack == null) {
return (R1) ret;
}
return callBack.apply(ret);
}
public static void main(String[] args) throws Exception {
Long aLong = retryFunction(t -> {
Long test = Long.valueOf(t);
if (test == 0) {
throw new RuntimeException("结果=0,重试");
}
return test;
}
, "1"
, NORMAL_RETRY_COUNT
, "测试"
, () -> new RuntimeException("测试")
, (r) -> {
if (r < 3) {
throw new RuntimeException("结果<3");
}
return r + 100;
});
System.out.println(aLong);
retryFunction(t -> System.out.println(Long.valueOf(t))
, "1q", NORMAL_RETRY_COUNT, "测试", () -> new RuntimeException("测试异常"));
}
}