diff --git a/README.md b/README.md index 5ef95d63..958eac17 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是 - 11、支持登录验证; - 12、GLUE:提供Web IDE,支持在线开发任务逻辑代码,动态发布,实时编译生效,省略部署上线的过程。支持30个版本的历史版本回溯。 - 12、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性; -- 14、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行; +- 14、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔; #### 1.3 发展 于2015年中,我在github上创建XXL-JOB项目仓库并提交第一个commit,随之进行系统结构设计,UI选型,交互设计…… @@ -565,7 +565,7 @@ XXL-JOB会为每次调度请求生成一个单独的日志文件,通过重写L - 3、【重要】在 “调度中心” 支持HA的基础上,扩展执行器的Failover支持,支持配置多执行期地址; #### 6.6 版本 V1.4.0 新特性 -- 1、任务依赖: 通过事件触发方式实现, 任务执行成功并回调时会主动触发一次子任务的调度; +- 1、任务依赖: 通过事件触发方式实现, 任务执行成功并回调时会主动触发一次子任务的调度, 多个子任务用逗号分隔; - 2、执行器底层实现代码进行重度重构, 优化底层建表脚本; - 3、执行器中任务线程分组逻辑优化: 之前根据执行器JobHandler进行线程分组,当多个任务复用Jobhanlder会导致相互阻塞。现改为根据调度中心任务进行任务线程分组,任务与任务执行相互隔离; - 4、执行器调度通讯方案优化, 通过Hex + HC实现建议RPC通讯协议, 优化了通讯参数的维护和解析流程; diff --git a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/callback/XxlJobLogCallbackServerHandler.java b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/callback/XxlJobLogCallbackServerHandler.java index cf3c6088..91193600 100644 --- a/xxl-job-admin/src/main/java/com/xxl/job/admin/core/callback/XxlJobLogCallbackServerHandler.java +++ b/xxl-job-admin/src/main/java/com/xxl/job/admin/core/callback/XxlJobLogCallbackServerHandler.java @@ -47,24 +47,32 @@ public class XxlJobLogCallbackServerHandler extends AbstractHandler { if (!ResponseModel.SUCCESS.equals(log.getHandleStatus())) { XxlJobInfo xxlJobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(log.getJobGroup(), log.getJobName()); if (xxlJobInfo!=null && StringUtils.isNotBlank(xxlJobInfo.getChildJobKey())) { - String[] jobKeyArr = xxlJobInfo.getChildJobKey().split("_"); - if (jobKeyArr!=null && jobKeyArr.length==2) { - XxlJobInfo childJobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(jobKeyArr[0], jobKeyArr[1]); - if (childJobInfo!=null) { - try { - boolean ret = DynamicSchedulerUtil.triggerJob(childJobInfo.getJobName(), childJobInfo.getJobGroup()); + childTriggerMsg = "
"; + String[] childJobKeys = xxlJobInfo.getChildJobKey().split(","); + for (int i = 0; i < childJobKeys.length; i++) { + String[] jobKeyArr = childJobKeys[i].split("_"); + if (jobKeyArr!=null && jobKeyArr.length==2) { + XxlJobInfo childJobInfo = DynamicSchedulerUtil.xxlJobInfoDao.load(jobKeyArr[0], jobKeyArr[1]); + if (childJobInfo!=null) { + try { + boolean ret = DynamicSchedulerUtil.triggerJob(childJobInfo.getJobName(), childJobInfo.getJobGroup()); - // add msg - childTriggerMsg += MessageFormat.format("
触发子任务成功, 子任务Key: {0}, status: {1}, 子任务描述: {2}", xxlJobInfo.getChildJobKey(), ret, childJobInfo.getJobDesc()); - } catch (SchedulerException e) { - logger.error("", e); + // add msg + childTriggerMsg += MessageFormat.format("
{0}/{1} 触发子任务成功, 子任务Key: {2}, status: {3}, 子任务描述: {4}", + (i+1), childJobKeys.length, childJobKeys[i], ret, childJobInfo.getJobDesc()); + } catch (SchedulerException e) { + logger.error("", e); + } + } else { + childTriggerMsg += MessageFormat.format("
{0}/{1} 触发子任务失败, 子任务xxlJobInfo不存在, 子任务Key: {2}", + (i+1), childJobKeys.length, childJobKeys[i]); } } else { - childTriggerMsg = "
触发子任务失败, 子任务xxlJobInfo不存在, 子任务Key:" + xxlJobInfo.getChildJobKey(); - } - } else { - childTriggerMsg = "
触发子任务失败, 子任务Key格式错误, 子任务Key:" + xxlJobInfo.getChildJobKey(); - } + childTriggerMsg += MessageFormat.format("
{0}/{1} 触发子任务失败, 子任务Key格式错误, 子任务Key: {2}", + (i+1), childJobKeys.length, childJobKeys[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 24eec955..f62f4d7a 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 @@ -17,6 +17,7 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import java.text.MessageFormat; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -86,6 +87,21 @@ public class XxlJobServiceImpl implements IXxlJobService { return new ReturnT(500, "请输入“JobHandler”"); } + // childJobKey valid + if (StringUtils.isNotBlank(childJobKey)) { + String[] childJobKeys = childJobKey.split(","); + for (String childJobKeyItem: childJobKeys) { + String[] childJobKeyArr = childJobKeyItem.split("_"); + if (childJobKeyArr.length!=2) { + return new ReturnT(500, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem)); + } + XxlJobInfo childJobInfo = xxlJobInfoDao.load(childJobKeyArr[0], childJobKeyArr[1]); + if (childJobInfo==null) { + return new ReturnT(500, MessageFormat.format("子任务Key({0})无效", childJobKeyItem)); + } + } + } + // generate jobName String jobName = FastDateFormat.getInstance("yyyyMMddHHmmssSSSS").format(new Date()); try { @@ -158,6 +174,21 @@ public class XxlJobServiceImpl implements IXxlJobService { return new ReturnT(500, "请输入“JobHandler”"); } + // childJobKey valid + if (StringUtils.isNotBlank(childJobKey)) { + String[] childJobKeys = childJobKey.split(","); + for (String childJobKeyItem: childJobKeys) { + String[] childJobKeyArr = childJobKeyItem.split("_"); + if (childJobKeyArr.length!=2) { + return new ReturnT(500, MessageFormat.format("子任务Key({0})格式错误", childJobKeyItem)); + } + XxlJobInfo childJobInfo = xxlJobInfoDao.load(childJobKeyArr[0], childJobKeyArr[1]); + if (childJobInfo==null) { + return new ReturnT(500, MessageFormat.format("子任务Key({0})无效", childJobKeyItem)); + } + } + } + // stage job info XxlJobInfo jobInfo = xxlJobInfoDao.load(jobGroup, jobName); jobInfo.setJobCron(jobCron); diff --git a/xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl b/xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl index b6b702da..54a433b9 100644 --- a/xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl +++ b/xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl @@ -151,7 +151,7 @@
-
+

@@ -232,7 +232,7 @@ public class DemoJobHandler extends IJobHandler {
-
+