Java方法重试的正确姿势

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);
    }

}

3、其他方案

https://github.com/rholder/guava-retrying

https://github.com/elennick/retry4j

猜你喜欢

转载自blog.csdn.net/w605283073/article/details/89038394