解决线程中的异常无法捕获问题,进行方法重试

方法重试工具类

package com.example.demo.aop;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import sun.security.krb5.internal.crypto.HmacMd5ArcFourCksumType;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 方法重试工具类
 * 场景描述:
 * 在项目启动时,有时候会遇到网络问题,配置同步问题等,导致启动失败,然后需要进行重试操作。
 * 而普通的aop,spring-retry,guaua-retry等都是针对主线程中发生异常时,去进行的重试操作。
 * 当异常发生在子线程内部时,则捕获不到,导致切面拦截不到异常,进而重试失败。
 *-----------------------------------------------
 * 此工具类为了解决在线程中发生异常时,进行重试操作。
 * @creator wangli66
 * @create-time 15:24 2019/11/20
 **/
public class RetryUtil1 {
    private static Logger logger = LoggerFactory.getLogger(RetryUtil1.class);
    private static volatile AtomicInteger retryTimes = new AtomicInteger(-1);
    private static final Integer DEFAULT_RETRY_TIMES = 1;
    private static final Long DEFAULT_SLEEP_TIME = 3000L;
    // 任务队列,先进先出
    private static volatile Queue<Task> taskQueue = new LinkedBlockingDeque<>();

    private static Set<String> threadNameSet = new HashSet<>();

    /*
     * @Description: 重试方法
     * @param: [objects],需要重试方法的入参
     * @return: void
     * @Date: 2019/11/20
     */
    public static void retry(@NonNull String mName, Integer number, Long inteval, Object... objects) {
        logger.info("进入retry的线程为:..." + Thread.currentThread().getName() + ",当前为第:" + retryTimes.get() + "次");

        if (retryTimes.get() == 0) {
            return;
        }
        // 初始化
        if (retryTimes.get() == -1) {
            if (number == null || number <= 0) {
                number = DEFAULT_RETRY_TIMES;
            }
            retryTimes.set(number);
        }


        if (inteval == null || inteval <= 0) {
            inteval = DEFAULT_SLEEP_TIME;
        }
        retryTimes.decrementAndGet();

        try {
            Thread.currentThread().sleep(inteval);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
        String targetClassName = stackTraces[2].getClassName();
        String targetMethod = stackTraces[2].getMethodName();
        if (targetClassName.contains("$") && !targetClassName.contains("lambda")) {
            targetClassName = targetClassName.substring(0, targetClassName.indexOf("$"));
            targetMethod = mName;
        }
        logger.info("目标类类名:" + targetClassName);
        logger.info("目标方法名:" + targetMethod);

        try {
            Class<?> aClass = Class.forName(targetClassName);
            Object targetObj = aClass.newInstance();
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
                logger.info("当前方法名:" + method.getName());
                method.setAccessible(true);
                if (null != method.getName() && targetMethod.equals(method.getName())) {
                    logger.info("调用方法:" + method.getName());
                    method.invoke(targetObj, objects);
                }
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public static synchronized void retryByTask(Task task) {
		// 使用方法签名,来判断是否同一方法,然后拿到终止条件
        if (taskQueue.size() == 0) {
//            if (threadNameSet.add(task.getTargetMethod())) {
            if (threadNameSet.add(task.getMethodSign())) {
                taskQueue.add(task);
                dealwithTask();
            }
        } else {
//            if(threadNameSet.add(task.getTargetMethod())) {
            if (threadNameSet.add(task.getMethodSign())) {
                taskQueue.add(task);
            }
        }
    }


    public static void dealwithTask() {
        Iterator<Task> iterator = taskQueue.iterator();
        while (iterator.hasNext()) {
            Task t = iterator.next();
            // 开始处理任务
            start(t);
            // 移除当前处理的任务
            iterator.remove();
        }

    }

    public static void start(Task t) {
        new Thread(() -> {
            threadNameSet.add(Thread.currentThread().getId() + "");
            for (int i = 0; i < t.getTimes(); i++) {
                try {
                    Thread.currentThread().sleep(t.getWaitTime());
                    Class<?> aClass = Class.forName(t.getTargetClass());
                    String targetMethod = t.getTargetMethod();
                    Object targetObj = aClass.newInstance();
                    Method[] declaredMethods = aClass.getDeclaredMethods();
                    for (Method method : declaredMethods) {
                        logger.info("当前方法名:" + method.getName());
                        method.setAccessible(true);
                        // 可能存在方法重载,导致方法名一直的情况
                        if (null != method.getName() && targetMethod.equals(method.getName())) {
                            logger.info("调用方法:" + method.getName());
                            Parameter[] parameters = method.getParameters();
                            if (compareArray(parameters, t.getArgs())) {
                                method.invoke(targetObj, t.getArgs());
                            }
                        }
                    }

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    System.out.println(e.getCause());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 当最后一次时,把该方法签名移除
//                if(i == (t.getTimes() - 1)) {
//                    threadNameSet.remove(t.getMethodSign());
//                }
            }

        }).start();
    }

    private static String getObjectType(String typeName) {
        if ("int".equals(typeName)) {
            return "java.lang.Integer";
        }
        if ("long".equals(typeName)) {
            return "java.lang.Long";
        }
        if ("double".equals(typeName)) {
            return "java.lang.Double";
        }
        if ("float".equals(typeName)) {
            return "java.lang.Float";
        }
        if ("byte".equals(typeName)) {
            return "java.lang.Byte";
        }
        if ("short".equals(typeName)) {
            return "java.lang.Short";
        }
        if ("char".equals(typeName)) {
            return "java.lang.Character";
        }
        if ("boolean".equals(typeName)) {
            return "java.lang.Boolean";
        }
        return null;
    }

    /*
     * @Description: 比较参数列表是否相等
     * @param: [objects1, objects2]
     * @return: boolean
     * @Date: 2019/11/22
     */
    public static boolean compareArray(Parameter[] objects1, Object[] objects2) {
        if(objects1.length == 0 && objects2.length == 0) {
            return true;
        }
        if(objects1.length == 0 || objects2.length == 0) {
            return false;
        }
        List<String> list1 = Arrays.stream(objects1)
                .filter((obj) -> obj == null ? false : true)
                .map((obj1) -> {
                    if (obj1.getType().isPrimitive()) {
                        return getObjectType(obj1.getType().getTypeName());
                    } else {
                        return obj1.getType().getTypeName().toString();
                    }
                })
                .collect(Collectors.toList());

        List<String> list2 = Arrays.stream(objects2)
                .filter((obj) -> obj == null ? false : true)
                .map((obj1) -> obj1.getClass().getTypeName())
                .collect(Collectors.toList());
        System.out.println("list1:" +list1+"\nlist2:"+list2);
        // 两个list引用相同(包括两者都为空指针的情况)
        if (list1 == list2) {
            return true;
        }

        // 两个list元素个数不相同
        if (list1.size() != list2.size()) {
            return false;
        }

        // 两个list元素个数已经相同,再比较两者内容
        // 采用这种可以忽略list中的元素的顺序
        // 涉及到对象的比较是否相同时,确保实现了equals()方法
        if (!list1.containsAll(list2)) {
            return false;
        }
        return true;

    }


}

任务类

package com.example.demo.aop;

/**
 * @creator wangli66
 * @create-time 10:32 2019/11/21
 **/
public class Task {
    private String targetClass;
    private String targetMethod;
    private Object[] args;
    private Integer times;
    private Long waitTime;
    private String methodSign;

    public String getMethodSign() {
        return methodSign;
    }

    public void setMethodSign(String methodSign) {
        this.methodSign = methodSign;
    }

    public Task() {
    }

    public Task(String targetClass, String targetMethod, Object[] args, Integer times, Long waitTime, String methodSign) {
        this.targetClass = targetClass;
        this.targetMethod = targetMethod;
        this.args = args;
        this.times = times;
        this.waitTime = waitTime;
        this.methodSign = methodSign;
    }

    public String getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(String targetClass) {
        this.targetClass = targetClass;
    }

    public String getTargetMethod() {
        return targetMethod;
    }

    public void setTargetMethod(String targetMethod) {
        this.targetMethod = targetMethod;
    }

    public Object[] getArgs() {
        return args;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }

    public Integer getTimes() {
        return times;
    }

    public void setTimes(Integer times) {
        this.times = times;
    }

    public Long getWaitTime() {
        return waitTime;
    }

    public void setWaitTime(Long waitTime) {
        this.waitTime = waitTime;
    }
}

自定义异常类

package com.example.demo.retry;

/**
 * @creator wangli66
 * @create-time 14:10 2019/11/21
 **/
public class RetryException extends RuntimeException {
    // 提供无参数的构造方法
    public RetryException() {
    }

    // 提供一个有参数的构造方法,可自动生成
    public RetryException(String message) {
        super(message);// 把参数传递给Throwable的带String参数的构造方法
    }

}

 工作类

package com.example.demo.retry;

import com.example.demo.aop.RetryAnno;
import com.example.demo.aop.RetryUtil1;
import com.example.demo.aop.Task;
import com.example.demo.myenum.UserTest;
import org.springframework.stereotype.Service;

import java.util.Arrays;

/**
 * @creator wangli66
 * @create-time 14:10 2019/11/21
 **/
@Service
public class Bizwork {

    public int work(int  i) {
        System.out.println("work.........有参:"+i);
        if(i == 1) {
            throw new RetryException("自定义异常");
        }
        return 0;
    }

    public void work1() {
        System.out.println("work........无参");
        new Thread(()-> {
            System.out.println("线程异常:"+Thread.currentThread().getId());
            try {
                throw new RetryException();
            } catch (RetryException e) {
                // 设置参数
                Task task = new Task();
                task.setTargetClass(Bizwork.class.getName());
                task.setTargetMethod("work1");
                task.setArgs(new Object[0]);
                task.setTimes(3);
                task.setWaitTime(3000L);
                task.setMethodSign(getMethodSign(null));
                System.out.println("方法签名:"+getMethodSign(null));
                // 调用重试工具类
                RetryUtil1.retryByTask(task);
            }
        }).start();
    }

    public String plus(int i1, int i2, UserTest userTest) {
        System.out.println("plus..........");
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("plus...run..........");
                try {
                    throw new RetryException();
                } catch (RetryException e) {
                    // 生成该方法签名
                    String methodSign = getMethodSign(i1,i2,userTest);
                    System.out.println("方法签名:"+methodSign);
                    // 调用重试工具类
                    RetryUtil1.retryByTask(new Task(
                            Bizwork.class.getName(),
                            "plus",
                            new Object[]{i1,i2,userTest},
                            1,
                            1000l,
                            methodSign));
                }
            }
        }).start();
        return null;
    }

    @RetryAnno(waitDesc = 3000L,maxAttempt = 3,retryThrowable = Exception.class)
    public void testAnno() {
        throw new RetryException("测试注解");
    }

    public String getMethodSign(Object ...objects) {
        StringBuffer sb = new StringBuffer();
        // 拿到方法的全类名
        String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
        // 拿到方法的参数列表,空的话为null
        sb.append(methodName).append("^");
        if(objects == null) {
            return sb.append("null").toString();
        }
        Arrays.stream(objects).forEach((obj) -> {
            if(obj == null ) {
                sb.append("null");
            }else {
                sb.append(obj.getClass().toString());
            }
        });
        return sb.toString();
    }
}

注解

package com.example.demo.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @creator wangli66
 * @create-time 20:41 2019/11/20
 **/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryAnno {
    int maxAttempt() default 0;
    long waitDesc() default 0;
    Class retryThrowable() default Exception.class;
}

AOP切面

 

package com.example.demo.retry;

import com.example.demo.aop.RetryAnno;
import com.example.demo.aop.RetryUtil1;
import com.example.demo.aop.Task;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 * @describtion aop异常通知
 * @creator wangli66
 * @create-time 14:19 2019/11/21
 **/
@Aspect
@Component
public class BizAspect {

    /*
     * @Description: 在注解中直接定义切点的方式
     * @param: [e]
     * @return: void
     * @Date: 2019/11/26
     */
    @AfterThrowing(throwing = "e",pointcut = "execution(* com.example.demo.retry.Bizwork.*(..))")
    public void testThrowing(Exception e) {
        System.out.println("aspect.........");
    }

    /*
     * @Description: 单独定义切点
     * @param: []
     * @return: void
     * @Date: 2019/11/26
     */
    @Pointcut("execution(* com.example.demo.retry.Bizwork.*(..))")
    public void workCut() {
    }

    /*
     * @Description: 引入切点
     * @param: [joinPoint]
     * @return: void
     * @Date: 2019/11/26
     */
    @AfterThrowing("workCut()")
    public void testPointCut(JoinPoint joinPoint) {
        System.out.println("testPointCut.......");
        Task task = new Task();
        // 设置参数
        task.setTargetClass(joinPoint.getSignature().getDeclaringTypeName());
        task.setTargetMethod(joinPoint.getSignature().getName());
        task.setArgs(joinPoint.getArgs());
        task.setTimes(3);
        task.setWaitTime(3000L);

        // 调用重试工具类
        RetryUtil1.retryByTask(task);
    }

    /*
     * @Description: 使用自定义注解方式
     * @param: []
     * @return: void
     * @Date: 2019/11/26
     */
    @Pointcut(value = "@annotation(com.example.demo.aop.RetryAnno)")
    public void testAnnoCut() {

    }

    /*
     * @Description: 注解方式
     * @param: [joinPoint]
     * @return: void
     * @Date: 2019/11/21
     */
    @AfterThrowing(value = "@annotation(com.example.demo.aop.RetryAnno) && @annotation(retryAnno)")
    public void testAnnotion(JoinPoint joinPoint,RetryAnno retryAnno) {
        System.out.println("----注解方式------------------------"+retryAnno.maxAttempt());
        Task task = new Task();
        // 设置参数
        task.setTargetClass(joinPoint.getSignature().getDeclaringTypeName());
        task.setTargetMethod(joinPoint.getSignature().getName());
        task.setArgs(joinPoint.getArgs());
        task.setTimes(retryAnno.maxAttempt());
        task.setWaitTime(retryAnno.waitDesc());
        // 调用重试工具类
        RetryUtil1.retryByTask(task);

    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值