java8的优雅-函数式方式纯手撸方法、函数重试

803 阅读4分钟

起因

有同事写上传,发现上传失败,然后调用别人上传接口还吃掉异常

特点

可以重试任何方法,可以回调,异常转移....

代码实现

/**
 * 方法重试
 * <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("测试异常"));

    }

}