基于Java反射的定时任务设计

一、使用场景

1、不需要立即执行、立即得到结果返回。

2、如果执行失败、需要有失败补偿机制。

3、和业务代码解耦,适用于不同的务场景。

4、调用接口的入参、出参 统计,方便查询。

二、执行顺序

1、业务逻辑中,需要调用外部接口时,将参数组装好,往任务表中插入一条任务记录。(主要包括 任务类型、需要执行的类、方法、参数 等)

2、使用定时任务(xxlJob或分布式worker)定时扫描任务表中待执行或执行失败(未超过最大重试次数)的任务。

3、拿到待执行任务后,采用反射思想 执行任务,并记录执行状态和执行结果。

三、代码示例

表设计(通用任务执行表)

主要字段

任务类型 、task_type

执行状态、exec_status(待执行、执行成功、执行失败)

执行的类、exec_class

执行的方法、exec_method

执行方法的参数、exec_param

执行结果、exec_result

重试次数、retry_times

 

核心代码

定时任务调度

/**
 * 执行通用任务
 */
public void doTaskList() {
    List<Task> taskList = taskMapper.selectTaskList();
    if (CollectionUtils.isEmpty(taskList)) {
        return;
    }
    for (Task task : taskList) {
        try {
            Integer retryTimes = task.getRetryTimes();
            if (retryTimes == 1) {
                Date updateTime = task.getGmtModified();
                // 第一次重试,执行时间和上次时间间隔至少5分钟
                if (updateTime.getTime() + 1000 * 60 * 5 > System.currentTimeMillis()) {
                    continue;
                }
            }
            if (retryTimes == 2) {
                Date updateTime = task.getGmtModified();
                // 第二次重试,执行时间和上次时间间隔至少30分钟
                if (updateTime.getTime() + 1000 * 60 * 30 > System.currentTimeMillis()) {
                    continue;
                }
            }
            service.doTaskExec(task);
        } catch (Exception e) {
           // 执行失败发送提醒邮件
        }
    }
}

反射执行

/**
 * 通用任务执行
 *
 * @param task 待执行的任务
 */
public void doTaskExec(Task task) throws ClassNotFoundException {
    String execClass = task.getExecClass();
    String execMethod = task.getExecMethod();
    String execParam = task.getExecParam();

    Class<?> clazz = Class.forName(execClass);
    Object object = ApplicationContextUtil.getBean(clazz);
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
        if (!method.getName().equals(execMethod)) {
            continue;
        }
        Class<?>[] paramTypes = method.getParameterTypes();
        Object[] objectValue = new Object[paramTypes.length];
        for (int i = 0; i < paramTypes.length; i++) {
            objectValue[i] = JSON.parseObject(execParam, paramTypes[i]);
        }
        Object execResult;
        try {
            execResult = reflection(object, clazz, execMethod, paramTypes, objectValue);
        } catch (Exception e) {
            log.error("外部接口返回异常:", e);
            processFailureExecResult(task, e.getMessage());
            return;
        }
        processExecResult(task, JSON.toJSONString(execResult));
    }
}

 

Tags: