diff --git "a/doc/XXL-JOB\345\256\230\346\226\271\346\226\207\346\241\243.md" "b/doc/XXL-JOB\345\256\230\346\226\271\346\226\207\346\241\243.md" index 1dcc6cb99076cd09a51b3291aa447eec2a0aa1a4..b5b63fc9492827757279b617a9d551d86e6b2e7c 100644 --- "a/doc/XXL-JOB\345\256\230\346\226\271\346\226\207\346\241\243.md" +++ "b/doc/XXL-JOB\345\256\230\346\226\271\346\226\207\346\241\243.md" @@ -33,17 +33,18 @@ XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是 - 17、Rolling实时日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志; - 18、GLUE:提供Web IDE,支持在线开发任务逻辑代码,动态发布,实时编译生效,省略部署上线的过程。支持30个版本的历史版本回溯。 - 19、脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python、NodeJS、PHP、PowerShell等类型脚本; -- 20、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔; -- 21、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行; -- 22、自定义任务参数:支持在线配置调度任务入参,即时生效; -- 23、调度线程池:调度系统多线程触发调度运行,确保调度精确执行,不被堵塞; -- 24、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性; -- 25、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件; -- 26、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用; -- 27、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等; -- 28、全异步:任务调度流程全异步化设计实现,如异步调度、异步运行、异步回调等,有效对密集调度进行流量削峰,理论上支持任意时长任务的运行; -- 29、跨平台:原生提供通用HTTP任务Handler(Bean任务,"HttpJobHandler");业务方只需要提供HTTP链接即可,不限制语言、平台; -- 30、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文; +- 20、命令行任务:原生提供通用命令行任务Handler(Bean任务,"CommandJobHandler");业务方只需要提供命令行即可; +- 21、任务依赖:支持配置子任务依赖,当父任务执行结束且执行成功后将会主动触发一次子任务的执行, 多个子任务用逗号分隔; +- 22、一致性:“调度中心”通过DB锁保证集群分布式调度的一致性, 一次任务调度只会触发一次执行; +- 23、自定义任务参数:支持在线配置调度任务入参,即时生效; +- 24、调度线程池:调度系统多线程触发调度运行,确保调度精确执行,不被堵塞; +- 25、数据加密:调度中心和执行器之间的通讯进行数据加密,提升调度信息安全性; +- 26、邮件报警:任务失败时支持邮件报警,支持配置多邮件地址群发报警邮件; +- 27、推送maven中央仓库: 将会把最新稳定版推送到maven中央仓库, 方便用户接入和使用; +- 28、运行报表:支持实时查看运行数据,如任务数量、调度次数、执行器数量等;以及调度报表,如调度日期分布图,调度成功分布图等; +- 29、全异步:任务调度流程全异步化设计实现,如异步调度、异步运行、异步回调等,有效对密集调度进行流量削峰,理论上支持任意时长任务的运行; +- 30、跨平台:原生提供通用HTTP任务Handler(Bean任务,"HttpJobHandler");业务方只需要提供HTTP链接即可,不限制语言、平台; +- 31、国际化:调度中心支持国际化设置,提供中文、英文两种可选语言,默认为中文; ### 1.3 发展 @@ -1017,10 +1018,13 @@ docker build -t xuxueli/xxl-job-admin ./xxl-job-admin docker run --name xxl-job-admin -p 8080:8080 -d xuxueli/xxl-job-admin ``` -### 5.20 避免任务重复执行 +### 5.20 避免任务重复执行 调度密集或者耗时任务可能会导致任务阻塞,集群情况下调度组件小概率情况下会重复触发; 针对上述情况,可以通过结合 "单机路由策略(如:第一台、一致性哈希)" + "阻塞策略(如:单机串行、丢弃后续调度)" 来规避,最终避免任务重复执行。 +### 5.21 命令行任务 +原生提供通用命令行任务Handler(Bean任务,"CommandJobHandler");业务方只需要提供命令行即可; +如任务参数 "pwd" 将会执行命令并输出数据; ## 六、版本更新日志 @@ -1333,7 +1337,7 @@ Tips: 历史版本(V1.3.x)目前已经Release至稳定版本, 进入维护阶段 - 7、任务RollingLog展示逻辑优化,修复超时任务无法查看的问题; - 8、任务状态优化,仅运行状态"NORMAL"任务关联至quartz,降低quartz底层数据存储与调度压力; - 9、任务状态规范:新增任务默认停止状态,任务更新时保持任务状态不变; -- 10、[迭代中]原生提供通用命令行任务Handler(Bean任务,"CommandJobHandler");业务方只需要提供命令行即可,可执行任意命令; +- 10、命令行任务:原生提供通用命令行任务Handler(Bean任务,"CommandJobHandler");业务方只需要提供命令行即可; - 11、[迭代中]docker镜像,并且推送docker镜像到中央仓库,更进一步实现产品开箱即用; diff --git "a/doc/XXL-JOB\346\236\266\346\236\204\345\233\276.pptx" "b/doc/XXL-JOB\346\236\266\346\236\204\345\233\276.pptx" index 143d63f80a0df6ebc911d10a9fefde4201fb75f4..8db1d68256016e141d05ceb460aa0c068e499501 100644 Binary files "a/doc/XXL-JOB\346\236\266\346\236\204\345\233\276.pptx" and "b/doc/XXL-JOB\346\236\266\346\236\204\345\233\276.pptx" differ diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-frameless/src/main/java/com/xuxueli/executor/sample/frameless/config/FrameLessXxlJobConfig.java b/xxl-job-executor-samples/xxl-job-executor-sample-frameless/src/main/java/com/xuxueli/executor/sample/frameless/config/FrameLessXxlJobConfig.java index 6cd757273f8989b206fc7cc55b0c0c483b08b9c9..28aa06addaeba0e8444780ad3c0378b88d687e52 100644 --- a/xxl-job-executor-samples/xxl-job-executor-sample-frameless/src/main/java/com/xuxueli/executor/sample/frameless/config/FrameLessXxlJobConfig.java +++ b/xxl-job-executor-samples/xxl-job-executor-sample-frameless/src/main/java/com/xuxueli/executor/sample/frameless/config/FrameLessXxlJobConfig.java @@ -1,5 +1,6 @@ package com.xuxueli.executor.sample.frameless.config; +import com.xuxueli.executor.sample.frameless.jobhandler.CommandJobHandler; import com.xuxueli.executor.sample.frameless.jobhandler.DemoJobHandler; import com.xuxueli.executor.sample.frameless.jobhandler.HttpJobHandler; import com.xuxueli.executor.sample.frameless.jobhandler.ShardingJobHandler; @@ -35,6 +36,7 @@ public class FrameLessXxlJobConfig { XxlJobExecutor.registJobHandler("demoJobHandler", new DemoJobHandler()); XxlJobExecutor.registJobHandler("shardingJobHandler", new ShardingJobHandler()); XxlJobExecutor.registJobHandler("httpJobHandler", new HttpJobHandler()); + XxlJobExecutor.registJobHandler("commandJobHandler", new CommandJobHandler()); // load executor prop Properties xxlJobProp = loadProperties("xxl-job-executor.properties"); diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-frameless/src/main/java/com/xuxueli/executor/sample/frameless/jobhandler/CommandJobHandler.java b/xxl-job-executor-samples/xxl-job-executor-sample-frameless/src/main/java/com/xuxueli/executor/sample/frameless/jobhandler/CommandJobHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..fb8281a112970164180f453f247e38b3f98c94b3 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-frameless/src/main/java/com/xuxueli/executor/sample/frameless/jobhandler/CommandJobHandler.java @@ -0,0 +1,54 @@ +package com.xuxueli.executor.sample.frameless.jobhandler; + +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.log.XxlJobLogger; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * 命令行任务 + * + * @author xuxueli 2018-09-16 03:48:34 + */ +public class CommandJobHandler extends IJobHandler { + + @Override + public ReturnT execute(String param) throws Exception { + String command = param; + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // command process + Process process = Runtime.getRuntime().exec(command); + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobLogger.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobLogger.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + return IJobHandler.SUCCESS; + } else { + return new ReturnT(IJobHandler.FAIL.getCode(), "command exit value("+exitValue+") is failed"); + } + } + +} diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/config/JFinalCoreConfig.java b/xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/config/JFinalCoreConfig.java index f3c15c86713472734d8420faf80765c1c987c3aa..4dff38f544dd7fddeba1ff91c5bc8a4f1edc629a 100644 --- a/xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/config/JFinalCoreConfig.java +++ b/xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/config/JFinalCoreConfig.java @@ -4,6 +4,7 @@ import com.jfinal.config.*; import com.jfinal.kit.Prop; import com.jfinal.kit.PropKit; import com.xuxueli.executor.sample.jfinal.controller.IndexController; +import com.xuxueli.executor.sample.jfinal.jobhandler.CommandJobHandler; import com.xuxueli.executor.sample.jfinal.jobhandler.DemoJobHandler; import com.xuxueli.executor.sample.jfinal.jobhandler.HttpJobHandler; import com.xuxueli.executor.sample.jfinal.jobhandler.ShardingJobHandler; @@ -25,6 +26,7 @@ public class JFinalCoreConfig extends JFinalConfig { XxlJobExecutor.registJobHandler("demoJobHandler", new DemoJobHandler()); XxlJobExecutor.registJobHandler("shardingJobHandler", new ShardingJobHandler()); XxlJobExecutor.registJobHandler("httpJobHandler", new HttpJobHandler()); + XxlJobExecutor.registJobHandler("commandJobHandler", new CommandJobHandler()); // load executor prop Prop xxlJobProp = PropKit.use("xxl-job-executor.properties"); diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/jobhandler/CommandJobHandler.java b/xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/jobhandler/CommandJobHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..b6646d280006643749fb53e3f61f0b0892a20068 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-jfinal/src/main/java/com/xuxueli/executor/sample/jfinal/jobhandler/CommandJobHandler.java @@ -0,0 +1,54 @@ +package com.xuxueli.executor.sample.jfinal.jobhandler; + +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.log.XxlJobLogger; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * 命令行任务 + * + * @author xuxueli 2018-09-16 03:48:34 + */ +public class CommandJobHandler extends IJobHandler { + + @Override + public ReturnT execute(String param) throws Exception { + String command = param; + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // command process + Process process = Runtime.getRuntime().exec(command); + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobLogger.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobLogger.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + return IJobHandler.SUCCESS; + } else { + return new ReturnT(IJobHandler.FAIL.getCode(), "command exit value("+exitValue+") is failed"); + } + } + +} diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/jobhandler/CommandJobHandler.java b/xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/jobhandler/CommandJobHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..adbebcf2591341da83b710b6ffbb5990a2a81d83 --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-nutz/src/main/java/com/xuxueli/executor/sample/nutz/jobhandler/CommandJobHandler.java @@ -0,0 +1,58 @@ +package com.xuxueli.executor.sample.nutz.jobhandler; + +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.handler.annotation.JobHandler; +import com.xxl.job.core.log.XxlJobLogger; +import org.nutz.ioc.loader.annotation.IocBean; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * 命令行任务 + * + * @author xuxueli 2018-09-16 03:48:34 + */ +@JobHandler(value="commandJobHandler") +@IocBean +public class CommandJobHandler extends IJobHandler { + + @Override + public ReturnT execute(String param) throws Exception { + String command = param; + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // command process + Process process = Runtime.getRuntime().exec(command); + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobLogger.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobLogger.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + return IJobHandler.SUCCESS; + } else { + return new ReturnT(IJobHandler.FAIL.getCode(), "command exit value("+exitValue+") is failed"); + } + } + +} diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/java/com/xxl/job/executor/service/jobhandler/CommandJobHandler.java b/xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/java/com/xxl/job/executor/service/jobhandler/CommandJobHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..bc70e91c1d43aca55ea4e480f94715de65a7d02d --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-spring/src/main/java/com/xxl/job/executor/service/jobhandler/CommandJobHandler.java @@ -0,0 +1,58 @@ +package com.xxl.job.executor.service.jobhandler; + +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.handler.annotation.JobHandler; +import com.xxl.job.core.log.XxlJobLogger; +import org.springframework.stereotype.Component; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * 命令行任务 + * + * @author xuxueli 2018-09-16 03:48:34 + */ +@JobHandler(value="commandJobHandler") +@Component +public class CommandJobHandler extends IJobHandler { + + @Override + public ReturnT execute(String param) throws Exception { + String command = param; + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // command process + Process process = Runtime.getRuntime().exec(command); + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobLogger.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobLogger.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + return IJobHandler.SUCCESS; + } else { + return new ReturnT(IJobHandler.FAIL.getCode(), "command exit value("+exitValue+") is failed"); + } + } + +} diff --git a/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/service/jobhandler/CommandJobHandler.java b/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/service/jobhandler/CommandJobHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..bc70e91c1d43aca55ea4e480f94715de65a7d02d --- /dev/null +++ b/xxl-job-executor-samples/xxl-job-executor-sample-springboot/src/main/java/com/xxl/job/executor/service/jobhandler/CommandJobHandler.java @@ -0,0 +1,58 @@ +package com.xxl.job.executor.service.jobhandler; + +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.handler.annotation.JobHandler; +import com.xxl.job.core.log.XxlJobLogger; +import org.springframework.stereotype.Component; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * 命令行任务 + * + * @author xuxueli 2018-09-16 03:48:34 + */ +@JobHandler(value="commandJobHandler") +@Component +public class CommandJobHandler extends IJobHandler { + + @Override + public ReturnT execute(String param) throws Exception { + String command = param; + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // command process + Process process = Runtime.getRuntime().exec(command); + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobLogger.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobLogger.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + return IJobHandler.SUCCESS; + } else { + return new ReturnT(IJobHandler.FAIL.getCode(), "command exit value("+exitValue+") is failed"); + } + } + +}