任务循环依赖问题修复,避免子任务与父任务重复导致的调度死循环;
This commit is contained in:
parent
5a1834d721
commit
4ce20c1038
|
@ -374,7 +374,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是
|
||||||
GLUE模式(Python):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "python" 脚本;
|
GLUE模式(Python):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "python" 脚本;
|
||||||
GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "nodejs" 脚本;
|
GLUE模式(NodeJS):任务以源码方式维护在调度中心;该模式的任务实际上是一段 "nodejs" 脚本;
|
||||||
- JobHandler:运行模式为 "BEAN模式" 时生效,对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值;
|
- JobHandler:运行模式为 "BEAN模式" 时生效,对应执行器中新开发的JobHandler类“@JobHandler”注解自定义的value值;
|
||||||
- 子任务Key:每个任务都拥有一个唯一的任务Key(任务Key可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务Key所对应的任务的一次主动调度。
|
- 子任务:每个任务都拥有一个唯一的任务ID(任务ID可以从任务列表获取),当本任务执行结束并且执行成功时,将会触发子任务ID所对应的任务的一次主动调度。
|
||||||
- 阻塞处理策略:调度过于密集执行器来不及处理时的处理策略;
|
- 阻塞处理策略:调度过于密集执行器来不及处理时的处理策略;
|
||||||
单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行;
|
单机串行(默认):调度请求进入单机执行器后,调度请求进入FIFO队列并以串行方式运行;
|
||||||
丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;
|
丢弃后续调度:调度请求进入单机执行器后,发现执行器存在运行的调度任务,本次请求将会被丢弃并标记为失败;
|
||||||
|
@ -699,7 +699,7 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
|
||||||
#### 5.4.9 调度日志
|
#### 5.4.9 调度日志
|
||||||
调度中心每次进行任务调度,都会记录一条任务日志,任务日志主要包括以下三部分内容:
|
调度中心每次进行任务调度,都会记录一条任务日志,任务日志主要包括以下三部分内容:
|
||||||
|
|
||||||
- 任务信息:包括“执行器地址”、“JobHandler”和“执行参数”等属性,点击JobKey可查看,根据这些参数,可以精确的定位任务执行的具体机器和任务代码;
|
- 任务信息:包括“执行器地址”、“JobHandler”和“执行参数”等属性,点击任务ID按钮可查看,根据这些参数,可以精确的定位任务执行的具体机器和任务代码;
|
||||||
- 调度信息:包括“调度时间”、“调度结果”和“调度日志”等,根据这些参数,可以了解“调度中心”发起调度请求时具体情况。
|
- 调度信息:包括“调度时间”、“调度结果”和“调度日志”等,根据这些参数,可以了解“调度中心”发起调度请求时具体情况。
|
||||||
- 执行信息:包括“执行时间”、“执行结果”和“执行日志”等,根据这些参数,可以了解在“执行器”端任务执行的具体情况;
|
- 执行信息:包括“执行时间”、“执行结果”和“执行日志”等,根据这些参数,可以了解在“执行器”端任务执行的具体情况;
|
||||||
|
|
||||||
|
@ -716,9 +716,9 @@ xxl-job-admin#com.xxl.job.admin.controller.JobApiController.callback
|
||||||
- 执行日志:任务执行过程中,业务代码中打印的完整执行日志,见“4.7 查看执行日志”;
|
- 执行日志:任务执行过程中,业务代码中打印的完整执行日志,见“4.7 查看执行日志”;
|
||||||
|
|
||||||
#### 5.4.10 任务依赖
|
#### 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、底层系统日志级别规范调整,清理遗留代码;
|
- 25、底层系统日志级别规范调整,清理遗留代码;
|
||||||
- 26、建表SQL优化,支持同步创建制定编码的库和表;
|
- 26、建表SQL优化,支持同步创建制定编码的库和表;
|
||||||
- 27、系统安全性优化,登陆Token写Cookie时进行MD5加密,同时Cookie启用HttpOnly;
|
- 27、系统安全性优化,登陆Token写Cookie时进行MD5加密,同时Cookie启用HttpOnly;
|
||||||
|
- 28、新增"任务ID"属性,移除"JobKey"属性,前者承担所有功能,方便后续增强任务依赖功能。
|
||||||
|
- 29、任务循环依赖问题修复,避免子任务与父任务重复导致的调度死循环;
|
||||||
|
|
||||||
### TODO LIST
|
### TODO LIST
|
||||||
- 1、任务权限管理:执行器为粒度分配权限,核心操作校验权限;
|
- 1、任务权限管理:执行器为粒度分配权限,核心操作校验权限;
|
||||||
|
@ -1118,6 +1119,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段
|
||||||
- 11、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件;
|
- 11、执行器Log清理功能:调度中心Log删除时同步删除执行器中的Log文件;
|
||||||
- 12、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性;
|
- 12、Bean模式任务,JobHandler自动从执行器中查询展示为下拉框,选择后自动填充任务名称等属性;
|
||||||
- 13、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走Quartz,单独建立MQ消息表,调度中心竞争触发;
|
- 13、API事件触发类型任务(更类似MQ消息)支持"动态传参、延时消费";该类型任务不走Quartz,单独建立MQ消息表,调度中心竞争触发;
|
||||||
|
- 14、任务依赖增强,新增任务类型 "流程任务",流程节点可挂载普通类型任务,承担任务依赖功能。现有子任务模型取消;需要考虑任务依赖死循环问题;
|
||||||
|
|
||||||
|
|
||||||
## 七、其他
|
## 七、其他
|
||||||
|
|
|
@ -167,7 +167,7 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_INFO` (
|
||||||
`glue_source` text COMMENT 'GLUE源代码',
|
`glue_source` text COMMENT 'GLUE源代码',
|
||||||
`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',
|
`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',
|
||||||
`glue_updatetime` datetime 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`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class XxlJobInfo {
|
||||||
private String glueRemark; // GLUE备注
|
private String glueRemark; // GLUE备注
|
||||||
private Date glueUpdatetime; // GLUE更新时间
|
private Date glueUpdatetime; // GLUE更新时间
|
||||||
|
|
||||||
private String childJobKey; // 子任务Key
|
private String childJobId; // 子任务ID,多个逗号分隔
|
||||||
|
|
||||||
// copy from quartz
|
// copy from quartz
|
||||||
private String jobStatus; // 任务状态 【base on quartz】
|
private String jobStatus; // 任务状态 【base on quartz】
|
||||||
|
@ -172,12 +172,12 @@ public class XxlJobInfo {
|
||||||
this.glueUpdatetime = glueUpdatetime;
|
this.glueUpdatetime = glueUpdatetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getChildJobKey() {
|
public String getChildJobId() {
|
||||||
return childJobKey;
|
return childJobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setChildJobKey(String childJobKey) {
|
public void setChildJobId(String childJobId) {
|
||||||
this.childJobKey = childJobKey;
|
this.childJobId = childJobId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getJobStatus() {
|
public String getJobStatus() {
|
||||||
|
|
|
@ -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.XxlJobInfo;
|
||||||
import com.xxl.job.admin.core.model.XxlJobLog;
|
import com.xxl.job.admin.core.model.XxlJobLog;
|
||||||
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
|
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.admin.core.util.MailUtil;
|
||||||
import com.xxl.job.core.biz.model.ReturnT;
|
import com.xxl.job.core.biz.model.ReturnT;
|
||||||
import com.xxl.job.core.handler.IJobHandler;
|
import com.xxl.job.core.handler.IJobHandler;
|
||||||
|
@ -125,7 +124,7 @@ public class JobFailMonitorHelper {
|
||||||
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
|
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
|
||||||
" <tr>\n" +
|
" <tr>\n" +
|
||||||
" <td>执行器</td>\n" +
|
" <td>执行器</td>\n" +
|
||||||
" <td>JobKey</td>\n" +
|
" <td>任务ID</td>\n" +
|
||||||
" <td>任务描述</td>\n" +
|
" <td>任务描述</td>\n" +
|
||||||
" <td>告警类型</td>\n" +
|
" <td>告警类型</td>\n" +
|
||||||
" </tr>\n" +
|
" </tr>\n" +
|
||||||
|
@ -156,7 +155,7 @@ public class JobFailMonitorHelper {
|
||||||
XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup()));
|
XxlJobGroup group = XxlJobDynamicScheduler.xxlJobGroupDao.load(Integer.valueOf(info.getJobGroup()));
|
||||||
|
|
||||||
String title = "调度中心监控报警";
|
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);
|
MailUtil.sendMail(email, title, content);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +1,7 @@
|
||||||
package com.xxl.job.admin.service.impl;
|
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.XxlJobInfo;
|
||||||
import com.xxl.job.admin.core.model.XxlJobLog;
|
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.XxlJobInfoDao;
|
||||||
import com.xxl.job.admin.dao.XxlJobLogDao;
|
import com.xxl.job.admin.dao.XxlJobLogDao;
|
||||||
import com.xxl.job.admin.dao.XxlJobRegistryDao;
|
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.biz.model.ReturnT;
|
||||||
import com.xxl.job.core.handler.IJobHandler;
|
import com.xxl.job.core.handler.IJobHandler;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.quartz.SchedulerException;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -68,21 +63,21 @@ public class AdminBizImpl implements AdminBiz {
|
||||||
String callbackMsg = null;
|
String callbackMsg = null;
|
||||||
if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {
|
if (IJobHandler.SUCCESS.getCode() == handleCallbackParam.getExecuteResult().getCode()) {
|
||||||
XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());
|
XxlJobInfo xxlJobInfo = xxlJobInfoDao.loadById(log.getJobId());
|
||||||
if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobKey())) {
|
if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobId())) {
|
||||||
callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发子任务<<<<<<<<<<< </span><br>";
|
callbackMsg = "<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发子任务<<<<<<<<<<< </span><br>";
|
||||||
|
|
||||||
String[] childJobKeys = xxlJobInfo.getChildJobKey().split(",");
|
String[] childJobIds = xxlJobInfo.getChildJobId().split(",");
|
||||||
for (int i = 0; i < childJobKeys.length; i++) {
|
for (int i = 0; i < childJobIds.length; i++) {
|
||||||
int childJobId = JobKeyUtil.parseJobId(childJobKeys[i]);
|
int childJobId = (StringUtils.isNotBlank(childJobIds[i]) && StringUtils.isNumeric(childJobIds[i]))?Integer.valueOf(childJobIds[i]):-1;
|
||||||
if (childJobId > 0) {
|
if (childJobId > 0) {
|
||||||
ReturnT<String> triggerChildResult = xxlJobService.triggerJob(childJobId);
|
ReturnT<String> triggerChildResult = xxlJobService.triggerJob(childJobId);
|
||||||
|
|
||||||
// add msg
|
// add msg
|
||||||
callbackMsg += MessageFormat.format("{0}/{1} [JobKey={2}], 触发{3}, 触发备注: {4} <br>",
|
callbackMsg += MessageFormat.format("{0}/{1} [任务ID={2}], 触发{3}, 触发备注: {4} <br>",
|
||||||
(i+1), childJobKeys.length, childJobKeys[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg());
|
(i+1), childJobIds.length, childJobIds[i], (triggerChildResult.getCode()==ReturnT.SUCCESS_CODE?"成功":"失败"), triggerChildResult.getMsg());
|
||||||
} else {
|
} else {
|
||||||
callbackMsg += MessageFormat.format(" {0}/{1} [JobKey={2}], 触发失败, 触发备注: JobKey格式错误 <br>",
|
callbackMsg += MessageFormat.format(" {0}/{1} [任务ID={2}], 触发失败, 触发备注: 任务ID格式错误 <br>",
|
||||||
(i+1), childJobKeys.length, childJobKeys[i]);
|
(i+1), childJobIds.length, childJobIds[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.model.XxlJobInfo;
|
||||||
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
|
import com.xxl.job.admin.core.route.ExecutorRouteStrategyEnum;
|
||||||
import com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler;
|
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.XxlJobGroupDao;
|
||||||
import com.xxl.job.admin.dao.XxlJobInfoDao;
|
import com.xxl.job.admin.dao.XxlJobInfoDao;
|
||||||
import com.xxl.job.admin.dao.XxlJobLogDao;
|
import com.xxl.job.admin.dao.XxlJobLogDao;
|
||||||
|
@ -104,19 +103,20 @@ public class XxlJobServiceImpl implements XxlJobService {
|
||||||
jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", ""));
|
jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
// childJobKey valid
|
// ChildJobId valid
|
||||||
if (StringUtils.isNotBlank(jobInfo.getChildJobKey())) {
|
if (StringUtils.isNotBlank(jobInfo.getChildJobId())) {
|
||||||
String[] childJobKeys = jobInfo.getChildJobKey().split(",");
|
String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(), ",");
|
||||||
for (String childJobKeyItem: childJobKeys) {
|
for (String childJobIdItem: childJobIds) {
|
||||||
int childJobId = JobKeyUtil.parseJobId(childJobKeyItem);
|
if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) {
|
||||||
if (childJobId <= 0) {
|
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem));
|
||||||
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem));
|
if (childJobInfo==null) {
|
||||||
}
|
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem));
|
||||||
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(childJobId);
|
}
|
||||||
if (childJobInfo==null) {
|
} else {
|
||||||
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})无效", childJobKeyItem));
|
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
|
||||||
}
|
}
|
||||||
|
|
||||||
// add in db
|
// add in db
|
||||||
|
@ -167,19 +167,24 @@ public class XxlJobServiceImpl implements XxlJobService {
|
||||||
return new ReturnT<String>(ReturnT.FAIL_CODE, "失败处理策略非法");
|
return new ReturnT<String>(ReturnT.FAIL_CODE, "失败处理策略非法");
|
||||||
}
|
}
|
||||||
|
|
||||||
// childJobKey valid
|
// ChildJobId valid
|
||||||
if (StringUtils.isNotBlank(jobInfo.getChildJobKey())) {
|
if (StringUtils.isNotBlank(jobInfo.getChildJobId())) {
|
||||||
String[] childJobKeys = jobInfo.getChildJobKey().split(",");
|
String[] childJobIds = StringUtils.split(jobInfo.getChildJobId(), ",");
|
||||||
for (String childJobKeyItem: childJobKeys) {
|
for (String childJobIdItem: childJobIds) {
|
||||||
int childJobId = JobKeyUtil.parseJobId(childJobKeyItem);
|
if (StringUtils.isNotBlank(childJobIdItem) && StringUtils.isNumeric(childJobIdItem)) {
|
||||||
if (childJobId <= 0) {
|
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.valueOf(childJobIdItem));
|
||||||
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem));
|
if (childJobInfo==null) {
|
||||||
}
|
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})无效", childJobIdItem));
|
||||||
XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(childJobId);
|
}
|
||||||
if (childJobInfo==null) {
|
// avoid cycle relate
|
||||||
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务Key({0})无效", childJobKeyItem));
|
if (childJobInfo.getId() == jobInfo.getId()) {
|
||||||
|
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})不可与父任务重复", childJobIdItem));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new ReturnT<String>(ReturnT.FAIL_CODE, MessageFormat.format("子任务ID({0})格式错误", childJobIdItem));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
jobInfo.setChildJobId(StringUtils.join(childJobIds, ","));
|
||||||
}
|
}
|
||||||
|
|
||||||
// stage job info
|
// stage job info
|
||||||
|
@ -198,7 +203,7 @@ public class XxlJobServiceImpl implements XxlJobService {
|
||||||
exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());
|
exists_jobInfo.setExecutorParam(jobInfo.getExecutorParam());
|
||||||
exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
|
exists_jobInfo.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
|
||||||
exists_jobInfo.setExecutorFailStrategy(jobInfo.getExecutorFailStrategy());
|
exists_jobInfo.setExecutorFailStrategy(jobInfo.getExecutorFailStrategy());
|
||||||
exists_jobInfo.setChildJobKey(jobInfo.getChildJobKey());
|
exists_jobInfo.setChildJobId(jobInfo.getChildJobId());
|
||||||
xxlJobInfoDao.update(exists_jobInfo);
|
xxlJobInfoDao.update(exists_jobInfo);
|
||||||
|
|
||||||
// fresh quartz
|
// fresh quartz
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<result column="glue_remark" property="glueRemark" />
|
<result column="glue_remark" property="glueRemark" />
|
||||||
<result column="glue_updatetime" property="glueUpdatetime" />
|
<result column="glue_updatetime" property="glueUpdatetime" />
|
||||||
|
|
||||||
<result column="child_jobkey" property="childJobKey" />
|
<result column="child_jobid" property="childJobId" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
t.glue_source,
|
t.glue_source,
|
||||||
t.glue_remark,
|
t.glue_remark,
|
||||||
t.glue_updatetime,
|
t.glue_updatetime,
|
||||||
t.child_jobkey
|
t.child_jobid
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="pageList" parameterType="java.util.HashMap" resultMap="XxlJobInfo">
|
<select id="pageList" parameterType="java.util.HashMap" resultMap="XxlJobInfo">
|
||||||
|
@ -97,7 +97,7 @@
|
||||||
glue_source,
|
glue_source,
|
||||||
glue_remark,
|
glue_remark,
|
||||||
glue_updatetime,
|
glue_updatetime,
|
||||||
child_jobkey
|
child_jobid
|
||||||
) VALUES (
|
) VALUES (
|
||||||
#{jobGroup},
|
#{jobGroup},
|
||||||
#{jobCron},
|
#{jobCron},
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
#{glueSource},
|
#{glueSource},
|
||||||
#{glueRemark},
|
#{glueRemark},
|
||||||
NOW(),
|
NOW(),
|
||||||
#{childJobKey}
|
#{childJobId}
|
||||||
);
|
);
|
||||||
<!--<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
|
<!--<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
|
||||||
SELECT LAST_INSERT_ID()
|
SELECT LAST_INSERT_ID()
|
||||||
|
@ -146,7 +146,7 @@
|
||||||
glue_source = #{glueSource},
|
glue_source = #{glueSource},
|
||||||
glue_remark = #{glueRemark},
|
glue_remark = #{glueRemark},
|
||||||
glue_updatetime = #{glueUpdatetime},
|
glue_updatetime = #{glueUpdatetime},
|
||||||
child_jobkey = #{childJobKey}
|
child_jobid = #{childJobId}
|
||||||
WHERE id = #{id}
|
WHERE id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
|
|
@ -66,9 +66,8 @@
|
||||||
<table id="job_list" class="table table-bordered table-striped" width="100%" >
|
<table id="job_list" class="table table-bordered table-striped" width="100%" >
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th name="id" >id</th>
|
<th name="id" >任务ID</th>
|
||||||
<th name="jobGroup" >jobGroup</th>
|
<th name="jobGroup" >jobGroup</th>
|
||||||
<th name="childJobKey" >JobKey</th>
|
|
||||||
<th name="jobDesc" >描述</th>
|
<th name="jobDesc" >描述</th>
|
||||||
<th name="glueType" >运行模式</th>
|
<th name="glueType" >运行模式</th>
|
||||||
<th name="executorParam" >任务参数</th>
|
<th name="executorParam" >任务参数</th>
|
||||||
|
@ -144,8 +143,8 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
|
<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
|
||||||
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
|
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
|
||||||
<label for="lastname" class="col-sm-2 control-label">子任务Key<font color="black">*</font></label>
|
<label for="lastname" class="col-sm-2 control-label">子任务ID<font color="black">*</font></label>
|
||||||
<div class="col-sm-4"><input type="text" class="form-control" name="childJobKey" placeholder="请输入子任务的任务Key,如存在多个逗号分隔" maxlength="100" ></div>
|
<div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="请输入子任务的任务ID,如存在多个逗号分隔" maxlength="100" ></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="firstname" class="col-sm-2 control-label">阻塞处理策略<font color="red">*</font></label>
|
<label for="firstname" class="col-sm-2 control-label">阻塞处理策略<font color="red">*</font></label>
|
||||||
|
@ -312,8 +311,8 @@ process.exit(0)
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
|
<label for="firstname" class="col-sm-2 control-label">执行参数<font color="black">*</font></label>
|
||||||
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
|
<div class="col-sm-4"><input type="text" class="form-control" name="executorParam" placeholder="请输入“执行参数”" maxlength="512" ></div>
|
||||||
<label for="lastname" class="col-sm-2 control-label">子任务Key<font color="black">*</font></label>
|
<label for="lastname" class="col-sm-2 control-label">子任务ID<font color="black">*</font></label>
|
||||||
<div class="col-sm-4"><input type="text" class="form-control" name="childJobKey" placeholder="请输入子任务的任务Key,如存在多个逗号分隔" maxlength="100" ></div>
|
<div class="col-sm-4"><input type="text" class="form-control" name="childJobId" placeholder="请输入子任务的任务ID,如存在多个逗号分隔" maxlength="100" ></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="firstname" class="col-sm-2 control-label">阻塞处理策略<font color="red">*</font></label>
|
<label for="firstname" class="col-sm-2 control-label">阻塞处理策略<font color="red">*</font></label>
|
||||||
|
|
|
@ -90,10 +90,8 @@
|
||||||
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
|
<table id="joblog_list" class="table table-bordered table-striped display" width="100%" >
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th name="id" >id</th>
|
<th name="jobId" >任务ID</th>
|
||||||
<th name="jobGroup" >执行器ID</th>
|
<th name="jobGroup" >执行器ID</th>
|
||||||
<th name="jobId" >任务ID</th>
|
|
||||||
<th name="JobKey" >JobKey</th>
|
|
||||||
<#--<th name="executorAddress" >执行器地址</th>
|
<#--<th name="executorAddress" >执行器地址</th>
|
||||||
<th name="glueType" >运行模式</th>
|
<th name="glueType" >运行模式</th>
|
||||||
<th name="executorParam" >任务参数</th>-->
|
<th name="executorParam" >任务参数</th>-->
|
||||||
|
|
|
@ -20,7 +20,7 @@ $(function() {
|
||||||
"ordering": false,
|
"ordering": false,
|
||||||
//"scrollX": true, // X轴滚动条,取消自适应
|
//"scrollX": true, // X轴滚动条,取消自适应
|
||||||
"columns": [
|
"columns": [
|
||||||
{ "data": 'id', "bSortable": false, "visible" : false},
|
{ "data": 'id', "bSortable": false, "visible" : true},
|
||||||
{
|
{
|
||||||
"data": 'jobGroup',
|
"data": 'jobGroup',
|
||||||
"visible" : false,
|
"visible" : false,
|
||||||
|
@ -34,15 +34,6 @@ $(function() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"data": 'childJobKey',
|
|
||||||
"width":'10%',
|
|
||||||
"visible" : true,
|
|
||||||
"render": function ( data, type, row ) {
|
|
||||||
var jobKey = row.jobGroup + "_" + row.id;
|
|
||||||
return jobKey;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "data": 'jobDesc', "visible" : true,"width":'20%'},
|
{ "data": 'jobDesc', "visible" : true,"width":'20%'},
|
||||||
{
|
{
|
||||||
"data": 'glueType',
|
"data": 'glueType',
|
||||||
|
@ -372,7 +363,7 @@ $(function() {
|
||||||
$('#updateModal .form select[name=executorRouteStrategy] option[value='+ row.executorRouteStrategy +']').prop('selected', true);
|
$('#updateModal .form select[name=executorRouteStrategy] option[value='+ row.executorRouteStrategy +']').prop('selected', true);
|
||||||
$("#updateModal .form input[name='executorHandler']").val( row.executorHandler );
|
$("#updateModal .form input[name='executorHandler']").val( row.executorHandler );
|
||||||
$("#updateModal .form input[name='executorParam']").val( row.executorParam );
|
$("#updateModal .form input[name='executorParam']").val( row.executorParam );
|
||||||
$("#updateModal .form input[name='childJobKey']").val( row.childJobKey );
|
$("#updateModal .form input[name='childJobId']").val( row.childJobId );
|
||||||
$('#updateModal .form select[name=executorBlockStrategy] option[value='+ row.executorBlockStrategy +']').prop('selected', true);
|
$('#updateModal .form select[name=executorBlockStrategy] option[value='+ row.executorBlockStrategy +']').prop('selected', true);
|
||||||
$('#updateModal .form select[name=executorFailStrategy] option[value='+ row.executorFailStrategy +']').prop('selected', true);
|
$('#updateModal .form select[name=executorFailStrategy] option[value='+ row.executorFailStrategy +']').prop('selected', true);
|
||||||
$('#updateModal .form select[name=glueType] option[value='+ row.glueType +']').prop('selected', true);
|
$('#updateModal .form select[name=glueType] option[value='+ row.glueType +']').prop('selected', true);
|
||||||
|
|
|
@ -89,54 +89,32 @@ $(function() {
|
||||||
"ordering": false,
|
"ordering": false,
|
||||||
//"scrollX": false,
|
//"scrollX": false,
|
||||||
"columns": [
|
"columns": [
|
||||||
{ "data": 'id', "bSortable": false, "visible" : false},
|
|
||||||
{ "data": 'jobGroup', "visible" : false},
|
|
||||||
{ "data": 'jobId', "visible" : false},
|
|
||||||
{
|
{
|
||||||
"data": 'JobKey',
|
"data": 'jobId',
|
||||||
"visible" : true,
|
"visible" : true,
|
||||||
"render": function ( data, type, row ) {
|
"render": function ( data, type, row ) {
|
||||||
var jobKey = row.jobGroup + "_" + row.jobId;
|
var glueTypeTitle = row.glueType;
|
||||||
|
if ('GLUE_GROOVY'==row.glueType) {
|
||||||
|
glueTypeTitle = "GLUE模式(Java)";
|
||||||
|
} else if ('GLUE_SHELL'==row.glueType) {
|
||||||
|
glueTypeTitle = "GLUE模式(Shell)";
|
||||||
|
} else if ('GLUE_PYTHON'==row.glueType) {
|
||||||
|
glueTypeTitle = "GLUE模式(Python)";
|
||||||
|
}else if ('GLUE_NODEJS'==row.glueType) {
|
||||||
|
glueTypeTitle = "GLUE模式(Nodejs)";
|
||||||
|
} else if ('BEAN'==row.glueType) {
|
||||||
|
glueTypeTitle = "BEAN模式:" + row.executorHandler;
|
||||||
|
}
|
||||||
|
|
||||||
var glueTypeTitle = row.glueType;
|
var temp = '';
|
||||||
if ('GLUE_GROOVY'==row.glueType) {
|
temp += '执行器地址:' + (row.executorAddress?row.executorAddress:'');
|
||||||
glueTypeTitle = "GLUE模式(Java)";
|
temp += '<br>运行模式:' + glueTypeTitle;
|
||||||
} else if ('GLUE_SHELL'==row.glueType) {
|
temp += '<br>任务参数:' + row.executorParam;
|
||||||
glueTypeTitle = "GLUE模式(Shell)";
|
|
||||||
} else if ('GLUE_PYTHON'==row.glueType) {
|
|
||||||
glueTypeTitle = "GLUE模式(Python)";
|
|
||||||
}else if ('GLUE_NODEJS'==row.glueType) {
|
|
||||||
glueTypeTitle = "GLUE模式(Nodejs)";
|
|
||||||
} else if ('BEAN'==row.glueType) {
|
|
||||||
glueTypeTitle = "BEAN模式:" + row.executorHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
var temp = '';
|
return '<a class="logTips" href="javascript:;" >'+ row.jobId +'<span style="display:none;">'+ temp +'</span></a>';
|
||||||
temp += '执行器地址:' + (row.executorAddress?row.executorAddress:'');
|
|
||||||
temp += '<br>运行模式:' + glueTypeTitle;
|
|
||||||
temp += '<br>任务参数:' + row.executorParam;
|
|
||||||
|
|
||||||
return '<a class="logTips" href="javascript:;" >'+ jobKey +'<span style="display:none;">'+ temp +'</span></a>';
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// { "data": 'executorAddress', "visible" : true},
|
{ "data": 'jobGroup', "visible" : false},
|
||||||
// {
|
|
||||||
// "data": 'glueType',
|
|
||||||
// "visible" : true,
|
|
||||||
// "render": function ( data, type, row ) {
|
|
||||||
// if ('GLUE_GROOVY'==row.glueType) {
|
|
||||||
// return "GLUE模式(Java)";
|
|
||||||
// } else if ('GLUE_SHELL'==row.glueType) {
|
|
||||||
// return "GLUE模式(Shell)";
|
|
||||||
// } else if ('GLUE_PYTHON'==row.glueType) {
|
|
||||||
// return "GLUE模式(Python)";
|
|
||||||
// } else if ('BEAN'==row.glueType) {
|
|
||||||
// return "BEAN模式:" + row.executorHandler;
|
|
||||||
// }
|
|
||||||
// return row.executorHandler;
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// { "data": 'executorParam', "visible" : true},
|
|
||||||
{
|
{
|
||||||
"data": 'triggerTime',
|
"data": 'triggerTime',
|
||||||
"render": function ( data, type, row ) {
|
"render": function ( data, type, row ) {
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class XxlJobInfoDaoTest {
|
||||||
info.setGlueType("setGlueType");
|
info.setGlueType("setGlueType");
|
||||||
info.setGlueSource("setGlueSource");
|
info.setGlueSource("setGlueSource");
|
||||||
info.setGlueRemark("setGlueRemark");
|
info.setGlueRemark("setGlueRemark");
|
||||||
info.setChildJobKey("setChildJobKey");
|
info.setChildJobId("1");
|
||||||
|
|
||||||
int count = xxlJobInfoDao.save(info);
|
int count = xxlJobInfoDao.save(info);
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public class XxlJobInfoDaoTest {
|
||||||
info2.setGlueSource("setGlueSource2");
|
info2.setGlueSource("setGlueSource2");
|
||||||
info2.setGlueRemark("setGlueRemark2");
|
info2.setGlueRemark("setGlueRemark2");
|
||||||
info2.setGlueUpdatetime(new Date());
|
info2.setGlueUpdatetime(new Date());
|
||||||
info2.setChildJobKey("setChildJobKey2");
|
info2.setChildJobId("1");
|
||||||
|
|
||||||
int item2 = xxlJobInfoDao.update(info2);
|
int item2 = xxlJobInfoDao.update(info2);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ public class MailUtilTest {
|
||||||
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
|
" <thead style=\"font-weight: bold;color: #ffffff;background-color: #ff8c00;\" >" +
|
||||||
" <tr>\n" +
|
" <tr>\n" +
|
||||||
" <td>执行器</td>\n" +
|
" <td>执行器</td>\n" +
|
||||||
" <td>JobKey</td>\n" +
|
" <td>任务ID</td>\n" +
|
||||||
" <td>任务描述</td>\n" +
|
" <td>任务描述</td>\n" +
|
||||||
" <td>告警类型</td>\n" +
|
" <td>告警类型</td>\n" +
|
||||||
" </tr>\n" +
|
" </tr>\n" +
|
||||||
|
@ -35,9 +35,10 @@ public class MailUtilTest {
|
||||||
" <tbody>\n" +
|
" <tbody>\n" +
|
||||||
"</table>";
|
"</table>";
|
||||||
|
|
||||||
mailBodyTemplate = MessageFormat.format(mailBodyTemplate, "执行器A", "1_1", "任务A1");
|
String title = "调度中心监控报警";
|
||||||
|
String content = MessageFormat.format(mailBodyTemplate, "执行器A", "01", "任务A1");
|
||||||
|
|
||||||
boolean ret = MailUtil.sendMail("931591021@qq.com", "调度中心监控报警" , mailBodyTemplate);
|
boolean ret = MailUtil.sendMail("931591021@qq.com", title, content);
|
||||||
System.out.println(ret);
|
System.out.println(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue