diff --git a/doc/XXL-JOB官方文档.md b/doc/XXL-JOB官方文档.md index 1e5e1982..d2aaa475 100644 --- a/doc/XXL-JOB官方文档.md +++ b/doc/XXL-JOB官方文档.md @@ -374,7 +374,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 GLUE模式(Python):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "python" 脚本; GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "nodejs" 脚本; - JobHandler:运行模式为 "BEAN模式" 时生效,对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值; - - 子任务Key:每个任务都拥有一个唯一的任务Key(任务Key可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务Key所对应的任务的一次主动调度。 + - 子任务:每个任务都拥有一个唯一的任务ID(任务ID可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务ID所对应的任务的一次主动调度。 - 阻塞处理策略:调度过于密集执行器来不及处理时的处理策略; 单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行; 丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败; @@ -699,7 +699,7 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback #### 5.4.9 调度日志 调度中心每次进行任务调度,都会记录一条任务日志,任务日志主要包括以下三部分内容: -- 任务信息:包括“执行器地址”、“JobHandler”和“执行参数”等属性,点击JobKey可查看,根据这些参数,可以精确的定位任务执行的具体机器和任务代码; +- 任务信息:包括“执行器地址”、“JobHandler”和“执行参数”等属性,点击任务ID按钮可查看,根据这些参数,可以精确的定位任务执行的具体机器和任务代码; - 调度信息:包括“调度时间”、“调度结果”和“调度日志”等,根据这些参数,可以了解“调度中心”发起调度请求时具体情况。 - 执行信息:包括“执行时间”、“执行结果”和“执行日志”等,根据这些参数,可以了解在“执行器”端任务执行的具体情况; @@ -716,9 +716,9 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback - 执行日志:任务执行过程中,业务代码中打印的完整执行日志,见“4.7 查看执行日志”; #### 5.4.10 任务依赖 -原理:XXL-JOB中每个任务都对应有一个任务Key,同时,每个任务支持设置属性“子任务Key”,因此,通过“任务Key”可以匹配任务依赖关系。 +原理:XXL-JOB中每个任务都对应有一个任务ID,同时,每个任务支持设置属性“子任务ID”,因此,通过“任务ID”可以匹配任务依赖关系。 -当父任务执行结束并且执行成功时,将会根据“子任务Key”匹配子任务依赖,如果匹配到子任务,将会主动触发一次子任务的执行。 +当父任务执行结束并且执行成功时,将会根据“子任务ID”匹配子任务依赖,如果匹配到子任务,将会主动触发一次子任务的执行。 在任务日志界面,点击任务的“执行备注”的“查看”按钮,可以看到匹配子任务以及触发子任务执行的日志信息,如无信息则表示未触发子任务执行,可参考下图。 @@ -1102,7 +1102,8 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段 - 25、底层系统日志级别规范调整,清理遗留代码; - 26、建表SQL优化,支持同步创建制定编码的库和表; - 27、系统安全性优化,登陆Token写Cookie时进行MD5加密,同时Cookie启用HttpOnly; - +- 28、新增"任务ID"属性,移除"JobKey"属性,前者承担所有功能,方便后续增强任务依赖功能。 +- 29、任务循环依赖问题修复,避免子任务与父任务重复导致的调度死循环; ### TODO LIST - 1、任务权限管理:执行器为粒度分配权限,核心操作校验权限; @@ -1118,6 +1119,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段 - 11、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件; - 12、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性; - 13、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走Quartz,单独建立MQ消息表,调度中心竞争触发; +- 14、任务依赖增强,新增任务类型 "流程任务",流程节点可挂载普通类型任务,承担任务依赖功能。现有子任务模型取消;需要考虑任务依赖死循环问题; ## 七、其他 diff --git a/doc/db/tables_xxl_job.sql b/doc/db/tables_xxl_job.sql index 6de19187..ed328959 100644 --- a/doc/db/tables_xxl_job.sql +++ b/doc/db/tables_xxl_job.sql @@ -167,7 +167,7 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_INFO` ( `glue_source` text COMMENT 'GLUE源代码', `glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注', `glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间', - `child_jobkey` varchar(255) DEFAULT NULL COMMENT '子任务Key', + `child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java index ec1138d6..51847ac1 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java @@ -31,7 +31,7 @@ public class XxlJobInfo { private String glueRemark; // GLUE备注 private Date glueUpdatetime; // GLUE更新时间 - private String childJobKey; // 子任务Key + private String childJobId; // 子任务ID,多个逗号分隔 // copy from quartz private String jobStatus; // 任务状态 【base on quartz】 @@ -172,12 +172,12 @@ public class XxlJobInfo { this.glueUpdatetime = glueUpdatetime; } - public String getChildJobKey() { - return childJobKey; + public String getChildJobId() { + return childJobId; } - public void setChildJobKey(String childJobKey) { - this.childJobKey = childJobKey; + public void setChildJobId(String childJobId) { + this.childJobId = childJobId; } public String getJobStatus() { diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java index 2695569c..c9f7f04d 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/thread/JobFailMonitorHelper.java @@ -4,7 +4,6 @@ import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; -import com.xxl.job.admin.core.util.JobKeyUtil; import com.xxl.job.admin.core.util.MailUtil; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.handler.IJobHandler; @@ -125,7 +124,7 @@ public class JobFailMonitorHelper { " " + " \n" + " 执行器\n" + - " JobKey\n" + + " 任务ID\n" + " 任务描述\n" + " 告警类型\n" + " \n" + @@ -156,7 +155,7 @@ public class JobFailMonitorHelper { XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup())); String title = "调度中心监控报警"; - String content = MessageFormat.format(mailBodyTemplate, group!=null?group.getTitle():"null", JobKeyUtil.formatJobKey(info), info.getJobDesc()); + String content = MessageFormat.format(mailBodyTemplate, group!=null?group.getTitle():"null", info.getId(), info.getJobDesc()); MailUtil.sendMail(email, title, content); } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/JobKeyUtil.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/JobKeyUtil.java deleted file mode 100644 index 7eaea1f4..00000000 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/util/JobKeyUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.xxl.job.admin.core.util; - -import com.xxl.job.admin.core.model.XxlJobInfo; -import org.apache.commons.lang3.StringUtils; - -/** - * job key util - * - * @author xuxueli 2017-12-22 18:48:45 - */ -public class JobKeyUtil { - - /** - * format job key - * - * @param xxlJobInfo - * @return - */ - public static String formatJobKey(XxlJobInfo xxlJobInfo){ - return String.valueOf(xxlJobInfo.getJobGroup()) - .concat("_").concat(String.valueOf(xxlJobInfo.getId())); - } - - /** - * parse jobId from JobKey - * - * @param jobKey - * @return - */ - public static int parseJobId(String jobKey){ - if (jobKey!=null && jobKey.trim().length()>0) { - String[] jobKeyArr = jobKey.split("_"); - if (jobKeyArr.length == 2) { - String jobIdStr = jobKeyArr[1]; - if (StringUtils.isNotBlank(jobIdStr) && StringUtils.isNumeric(jobIdStr)) { - int jobId = Integer.valueOf(jobIdStr); - return jobId; - } - } - } - return -1; - } - -} diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java index 6f989b80..352b4c95 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/AdminBizImpl.java @@ -1,11 +1,7 @@ package com.xxl.job.admin.service.impl; -import com.xxl.job.admin.controller.JobApiController; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.model.XxlJobLog; -import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; -import com.xxl.job.admin.core.trigger.XxlJobTrigger; -import com.xxl.job.admin.core.util.JobKeyUtil; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobLogDao; import com.xxl.job.admin.dao.XxlJobRegistryDao; @@ -16,7 +12,6 @@ import com.xxl.job.core.biz.model.RegistryParam; import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.handler.IJobHandler; import org.apache.commons.lang3.StringUtils; -import org.quartz.SchedulerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; @@ -68,21 +63,21 @@ public class AdminBizImpl implements AdminBiz { String callbackMsg = null; if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) { XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId()); - if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobKey())) { + if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobId())) { callbackMsg = "

>>>>>>>>>>>触发子任务<<<<<<<<<<<
"; - String[] childJobKeys = xxlJobInfo.getChildJobKey().split(","); - for (int i = 0; i < childJobKeys.length; i++) { - int childJobId = JobKeyUtil.parseJobId(childJobKeys[i]); + String[] childJobIds = xxlJobInfo.getChildJobId().split(","); + for (int i = 0; i < childJobIds.length; i++) { + int childJobId = (StringUtils.isNotBlank(childJobIds[i]) && StringUtils.isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1; if (childJobId > 0) { ReturnT triggerChildResult = xxlJobService.triggerJob(childJobId); // add msg - callbackMsg += MessageFormat.format("{0}/{1} [JobKey={2}], 触发{3}, 触发备注: {4}
", - (i+1), childJobKeys.length, childJobKeys[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg()); + callbackMsg += MessageFormat.format("{0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4}
", + (i+1), childJobIds.length, childJobIds[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg()); } else { - callbackMsg += MessageFormat.format(" {0}/{1} [JobKey={2}], 触发失败, 触发备注: JobKey格式错误
", - (i+1), childJobKeys.length, childJobKeys[i]); + callbackMsg += MessageFormat.format(" {0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误
", + (i+1), childJobIds.length, childJobIds[i]); } } diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java index 820e8968..eecef342 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java @@ -5,7 +5,6 @@ import com.xxl.job.admin.core.model.XxlJobGroup; import com.xxl.job.admin.core.model.XxlJobInfo; import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum; import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler; -import com.xxl.job.admin.core.util.JobKeyUtil; import com.xxl.job.admin.dao.XxlJobGroupDao; import com.xxl.job.admin.dao.XxlJobInfoDao; import com.xxl.job.admin.dao.XxlJobLogDao; @@ -104,19 +103,20 @@ public class XxlJobServiceImpl implements XxlJobService { jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", "")); } - // childJobKey valid - if (StringUtils.isNotBlank(jobInfo.getChildJobKey())) { - String[] childJobKeys = jobInfo.getChildJobKey().split(","); - for (String childJobKeyItem: childJobKeys) { - int childJobId = JobKeyUtil.parseJobId(childJobKeyItem); - if (childJobId <= 0) { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem)); - } - XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(childJobId); - if (childJobInfo==null) { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})无效", childJobKeyItem)); + // ChildJobId valid + if (StringUtils.isNotBlank(jobInfo.getChildJobId())) { + String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(), ","); + for (String childJobIdItem: childJobIds) { + if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) { + XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem)); + if (childJobInfo==null) { + return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem)); + } + } else { + return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem)); } } + jobInfo.setChildJobId(StringUtils.join(childJobIds, ",")); } // add in db @@ -167,19 +167,24 @@ public class XxlJobServiceImpl implements XxlJobService { return new ReturnT(ReturnT.FAIL_CODE, "失败处理策略非法"); } - // childJobKey valid - if (StringUtils.isNotBlank(jobInfo.getChildJobKey())) { - String[] childJobKeys = jobInfo.getChildJobKey().split(","); - for (String childJobKeyItem: childJobKeys) { - int childJobId = JobKeyUtil.parseJobId(childJobKeyItem); - if (childJobId <= 0) { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem)); - } - XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(childJobId); - if (childJobInfo==null) { - return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})无效", childJobKeyItem)); + // ChildJobId valid + if (StringUtils.isNotBlank(jobInfo.getChildJobId())) { + String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(), ","); + for (String childJobIdItem: childJobIds) { + if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) { + XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem)); + if (childJobInfo==null) { + return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem)); + } + // avoid cycle relate + if (childJobInfo.getId() == jobInfo.getId()) { + return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})不可与父任务重复", childJobIdItem)); + } + } else { + return new ReturnT(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem)); } } + jobInfo.setChildJobId(StringUtils.join(childJobIds, ",")); } // stage job info @@ -198,7 +203,7 @@ public class XxlJobServiceImpl implements XxlJobService { exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam()); exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy()); exists_jobInfo.setExecutorFailStrategy(jobInfo.getExecutorFailStrategy()); - exists_jobInfo.setChildJobKey(jobInfo.getChildJobKey()); + exists_jobInfo.setChildJobId(jobInfo.getChildJobId()); xxlJobInfoDao.update(exists_jobInfo); // fresh quartz diff --git a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml b/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml index 9be9b8e0..a5c3abd0 100644 --- a/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml +++ b/xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml @@ -27,7 +27,7 @@ - + @@ -48,7 +48,7 @@ t.glue_source, t.glue_remark, t.glue_updatetime, - t.child_jobkey + t.child_jobid