Skip to content
项目
群组
代码片段
帮助
正在加载...
登录
切换导航
X
XXL-JOB
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
靳帅
XXL-JOB
Commits
935cc127
提交
935cc127
authored
4月 27, 2017
作者:
xueli.xue
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
脚本任务实现:Shell和Python
上级
7a129468
显示空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
115 行增加
和
100 行删除
+115
-100
jobinfo.index.ftl
...rc/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl
+7
-9
ExecutorBizImpl.java
.../main/java/com/xxl/job/core/biz/impl/ExecutorBizImpl.java
+18
-57
ScriptJobHandler.java
.../java/com/xxl/job/core/handler/impl/ScriptJobHandler.java
+55
-0
ScriptUtil.java
...-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java
+35
-34
没有找到文件。
xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl
浏览文件 @
935cc127
...
...
@@ -193,17 +193,15 @@ public class DemoGlueJobHandler extends IJobHandler {
<textarea
class=
"glueSource_shell"
style=
"display:none;"
>
#!/bin/bash
echo
hello shell
echo
"xxl-job: hello shell"
for
x in 1 2 3 4
for
item in 1 2 3
do
echo
number=$x
echo
"shell : $item"
sleep 1s
done
echo1 111
printf 666
echo2 222
echo "Good bye!"
</textarea>
<textarea
class=
"glueSource_python"
style=
"display:none;"
>
#!/usr/bin/python
...
...
@@ -214,11 +212,11 @@ import time
logging.basicConfig(level=logging.DEBUG)
logging.info('hello python')
logging.info('
xxl-job:
hello python')
for num in range(
0
, 3):
for num in range(
1
, 3):
time.sleep(1)
logging.info('
当前序号
:' + str(num) )
logging.info('
python
:' + str(num) )
logging.info('Good bye!')
</textarea>
...
...
xxl-job-core/src/main/java/com/xxl/job/core/biz/impl/ExecutorBizImpl.java
浏览文件 @
935cc127
...
...
@@ -9,15 +9,12 @@ import com.xxl.job.core.glue.GlueFactory;
import
com.xxl.job.core.glue.GlueTypeEnum
;
import
com.xxl.job.core.handler.IJobHandler
;
import
com.xxl.job.core.handler.impl.GlueJobHandler
;
import
com.xxl.job.core.handler.impl.ScriptJobHandler
;
import
com.xxl.job.core.log.XxlJobFileAppender
;
import
com.xxl.job.core.thread.JobThread
;
import
com.xxl.job.core.util.ScriptUtil
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
import
java.util.Date
;
/**
...
...
@@ -85,11 +82,11 @@ public class ExecutorBizImpl implements ExecutorBiz {
}
else
if
(
GlueTypeEnum
.
GLUE_GROOVY
==
GlueTypeEnum
.
match
(
triggerParam
.
getGlueType
()))
{
// valid exists job thread:change handler or glue
timeout
, need kill old thread
// valid exists job thread:change handler or glue
source updated
, need kill old thread
if
(
jobThread
!=
null
&&
!(
jobThread
.
getHandler
()
instanceof
GlueJobHandler
&&
((
GlueJobHandler
)
jobThread
.
getHandler
()).
getGlueUpdatetime
()==
triggerParam
.
getGlueUpdatetime
()
))
{
// change glue model or glue
timeout
, kill old job thread
// change glue model or glue
source updated
, kill old job thread
jobThread
.
toStop
(
"更换任务模式或JobHandler,终止旧任务线程"
);
jobThread
.
interrupt
();
XxlJobExecutor
.
removeJobThread
(
triggerParam
.
getJobId
());
...
...
@@ -107,61 +104,25 @@ public class ExecutorBizImpl implements ExecutorBiz {
}
jobThread
=
XxlJobExecutor
.
registJobThread
(
triggerParam
.
getJobId
(),
new
GlueJobHandler
(
jobHandler
,
triggerParam
.
getGlueUpdatetime
()));
}
}
else
if
(
GlueTypeEnum
.
GLUE_SHELL
==
GlueTypeEnum
.
match
(
triggerParam
.
getGlueType
()))
{
// make path
String
scriptPath
=
XxlJobFileAppender
.
filePath
+
"gluesource/"
;
String
scriptFileName
=
triggerParam
.
getJobId
()
+
"_"
+
triggerParam
.
getGlueUpdatetime
()
+
".sh"
;
// valid file
File
scriptFile
=
new
File
(
scriptPath
,
scriptFileName
);
if
(!
scriptFile
.
exists
())
{
// valid glue source
if
(
triggerParam
.
getGlueSource
()==
null
)
{
return
new
ReturnT
<
String
>(
ReturnT
.
FAIL_CODE
,
"glueSource is null."
);
}
// .../gluesource/
File
scriptPathDir
=
new
File
(
scriptPath
);
if
(!
scriptPathDir
.
exists
())
{
scriptPathDir
.
mkdirs
();
}
}
else
if
(
GlueTypeEnum
.
GLUE_SHELL
==
GlueTypeEnum
.
match
(
triggerParam
.
getGlueType
())
||
GlueTypeEnum
.
GLUE_PYTHON
==
GlueTypeEnum
.
match
(
triggerParam
.
getGlueType
())
)
{
// .../gluesource/666-156465656.sh
scriptFile
=
new
File
(
scriptPath
,
scriptFileName
);
FileOutputStream
fos
=
null
;
try
{
scriptFile
.
createNewFile
();
fos
=
new
FileOutputStream
(
scriptFile
,
true
);
fos
.
write
(
triggerParam
.
getGlueSource
().
getBytes
(
"utf-8"
));
fos
.
flush
();
}
catch
(
IOException
e
)
{
logger
.
error
(
e
.
getMessage
(),
e
);
return
new
ReturnT
<
String
>(
ReturnT
.
FAIL_CODE
,
e
.
getMessage
());
}
finally
{
if
(
fos
!=
null
)
{
try
{
fos
.
close
();
}
catch
(
IOException
e
)
{
logger
.
error
(
e
.
getMessage
(),
e
);
}
}
// valid exists job thread:change script or gluesource updated, need kill old thread
if
(
jobThread
!=
null
&&
!(
jobThread
.
getHandler
()
instanceof
ScriptJobHandler
&&
((
ScriptJobHandler
)
jobThread
.
getHandler
()).
getGlueUpdatetime
()==
triggerParam
.
getGlueUpdatetime
()
))
{
// change glue model or gluesource updated, kill old job thread
jobThread
.
toStop
(
"更换任务模式或JobHandler,终止旧任务线程"
);
jobThread
.
interrupt
();
XxlJobExecutor
.
removeJobThread
(
triggerParam
.
getJobId
());
jobThread
=
null
;
}
// make thread: new or exists invalid
if
(
jobThread
==
null
)
{
ScriptJobHandler
scriptJobHandler
=
new
ScriptJobHandler
(
triggerParam
.
getJobId
(),
triggerParam
.
getGlueUpdatetime
(),
triggerParam
.
getGlueSource
(),
GlueTypeEnum
.
match
(
triggerParam
.
getGlueType
()));
jobThread
=
XxlJobExecutor
.
registJobThread
(
triggerParam
.
getJobId
(),
scriptJobHandler
);
}
// log File
String
logFileName
=
XxlJobFileAppender
.
makeLogFileName
(
new
Date
(
triggerParam
.
getLogDateTim
()),
triggerParam
.
getLogId
());
// run script
ScriptUtil
.
execToFile
(
"python"
,
scriptFile
.
getName
(),
(
XxlJobFileAppender
.
filePath
+
logFileName
)
);
return
ReturnT
.
FAIL
;
}
else
if
(
GlueTypeEnum
.
GLUE_PYTHON
==
GlueTypeEnum
.
match
(
triggerParam
.
getGlueType
()))
{
String
scriptFilePath
=
XxlJobFileAppender
.
filePath
+
"gluesource/"
+
triggerParam
.
getJobId
()
+
"_"
+
triggerParam
.
getGlueUpdatetime
()
+
".py"
;
}
else
{
return
new
ReturnT
<
String
>(
ReturnT
.
FAIL_CODE
,
"glueType["
+
triggerParam
.
getGlueType
()
+
"] is not valid."
);
}
...
...
xxl-job-core/src/main/java/com/xxl/job/core/handler/impl/ScriptJobHandler.java
0 → 100644
浏览文件 @
935cc127
package
com
.
xxl
.
job
.
core
.
handler
.
impl
;
import
com.xxl.job.core.biz.model.ReturnT
;
import
com.xxl.job.core.glue.GlueTypeEnum
;
import
com.xxl.job.core.handler.IJobHandler
;
import
com.xxl.job.core.log.XxlJobFileAppender
;
import
com.xxl.job.core.util.ScriptUtil
;
/**
* Created by xuxueli on 17/4/27.
*/
public
class
ScriptJobHandler
extends
IJobHandler
{
private
int
jobId
;
private
long
glueUpdatetime
;
private
String
gluesource
;
private
GlueTypeEnum
glueType
;
public
ScriptJobHandler
(
int
jobId
,
long
glueUpdatetime
,
String
gluesource
,
GlueTypeEnum
glueType
){
this
.
jobId
=
jobId
;
this
.
glueUpdatetime
=
glueUpdatetime
;
this
.
gluesource
=
gluesource
;
this
.
glueType
=
glueType
;
}
public
long
getGlueUpdatetime
()
{
return
glueUpdatetime
;
}
@Override
public
ReturnT
<
String
>
execute
(
String
...
params
)
throws
Exception
{
// cmd + script-file-name
String
cmd
=
"bash"
;
String
scriptFileName
=
null
;
if
(
GlueTypeEnum
.
GLUE_SHELL
==
glueType
)
{
cmd
=
"bash"
;
scriptFileName
=
XxlJobFileAppender
.
filePath
.
concat
(
"gluesource/"
).
concat
(
String
.
valueOf
(
jobId
)).
concat
(
"_"
).
concat
(
String
.
valueOf
(
glueUpdatetime
)).
concat
(
".sh"
);
}
else
if
(
GlueTypeEnum
.
GLUE_PYTHON
==
glueType
)
{
cmd
=
"python"
;
scriptFileName
=
XxlJobFileAppender
.
filePath
.
concat
(
"gluesource/"
).
concat
(
String
.
valueOf
(
jobId
)).
concat
(
"_"
).
concat
(
String
.
valueOf
(
glueUpdatetime
)).
concat
(
".py"
);
}
// make script file
ScriptUtil
.
markScriptFile
(
scriptFileName
,
gluesource
);
// log file
String
logFileName
=
XxlJobFileAppender
.
filePath
.
concat
(
XxlJobFileAppender
.
contextHolder
.
get
());
// invoke
ScriptUtil
.
execToFile
(
cmd
,
scriptFileName
,
logFileName
);
return
ReturnT
.
SUCCESS
;
}
}
xxl-job-core/src/main/java/com/xxl/job/core/util/ScriptUtil.java
浏览文件 @
935cc127
package
com
.
xxl
.
job
.
core
.
util
;
import
com.xxl.job.core.log.XxlJobFileAppender
;
import
org.apache.commons.exec.CommandLine
;
import
org.apache.commons.exec.DefaultExecutor
;
import
org.apache.commons.exec.PumpStreamHandler
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.IOException
;
/**
* 1、内嵌编译器如"PythonInterpreter"无法引用扩展包,因此推荐使用java调用控制台进程方式"Runtime.getRuntime().exec()"来运行脚本(shell或python);
* 2、因为通过java调用控制台进程方式实现,需要保证目标机器PATH路径正确配置对应编译器;
* 3、暂时脚本执行日志只能在脚本执行结束后一次性获取,无法保证实时性;因此为确保日志实时性,可改为将脚本打印的日志存储在指定的日志文件上;
*
* 知识点:
* 1、日志输出到日志文件:[>>logfile 2>&1]:将错误输出2以及标准输出1都一起以附加写方式导入logfile文件
* 2、python 异常输出优先级高于标准输出,体现在Log文件中,因此推荐通过logging方式打日志保持和异常信息一致;否则用prinf日志顺序会错乱
* 4、python 异常输出优先级高于标准输出,体现在Log文件中,因此推荐通过logging方式打日志保持和异常信息一致;否则用prinf日志顺序会错乱
*
* Created by xuxueli on 17/2/25.
*/
public
class
ScriptUtil
{
private
static
String
pyCmd
=
"python"
;
private
static
String
shllCmd
=
"bash"
;
private
static
String
pyFile
=
"/Users/xuxueli/workspaces/idea-git-workspace/github/xxl-incubator/xxl-util/src/main/resources/script/pytest.py"
;
private
static
String
shellFile
=
"/Users/xuxueli/workspaces/idea-git-workspace/github/xxl-incubator/xxl-util/src/main/resources/script/shelltest.sh"
;
private
static
String
pyLogFile
=
"/Users/xuxueli/Downloads/tmp/pylog.log"
;
private
static
String
shLogFile
=
"/Users/xuxueli/Downloads/tmp/shlog.log"
;
public
static
void
main
(
String
[]
args
)
{
String
command
=
pyCmd
;
String
filename
=
pyFile
;
String
logFile
=
pyLogFile
;
if
(
false
)
{
command
=
shllCmd
;
filename
=
shellFile
;
logFile
=
shLogFile
;
/**
* make script file
*
* @param scriptFileName
* @param content
* @throws IOException
*/
public
static
void
markScriptFile
(
String
scriptFileName
,
String
content
)
throws
IOException
{
// filePath/
File
filePathDir
=
new
File
(
XxlJobFileAppender
.
filePath
);
if
(!
filePathDir
.
exists
())
{
filePathDir
.
mkdirs
();
}
execToFile
(
command
,
filename
,
logFile
);
// filePath/gluesource/
File
filePathSourceDir
=
new
File
(
filePathDir
,
"gluesource"
);
if
(!
filePathSourceDir
.
exists
())
{
filePathSourceDir
.
mkdirs
();
}
public
static
File
markScriptFile
(){
return
null
;
// make file, filePath/gluesource/666-123456789.py
FileOutputStream
fileOutputStream
=
null
;
try
{
fileOutputStream
=
new
FileOutputStream
(
scriptFileName
);
fileOutputStream
.
write
(
content
.
getBytes
(
"UTF-8"
));
fileOutputStream
.
close
();
}
catch
(
Exception
e
)
{
throw
e
;
}
finally
{
if
(
fileOutputStream
!=
null
){
fileOutputStream
.
close
();
}
}
}
/**
...
...
@@ -58,12 +66,11 @@ public class ScriptUtil {
* @param scriptFile
* @param logFile
*/
public
static
void
execToFile
(
String
command
,
String
scriptFile
,
String
logFile
){
try
{
public
static
void
execToFile
(
String
command
,
String
scriptFile
,
String
logFile
)
throws
IOException
{
// 标准输出:print (null if watchdog timeout)
// 错误输出:logging + 异常 (still exists if watchdog timeout)
// 标准输出
FileOutputStream
fileOutputStream
=
new
FileOutputStream
(
logFil
e
);
// 标准输入
FileOutputStream
fileOutputStream
=
new
FileOutputStream
(
logFile
,
tru
e
);
PumpStreamHandler
streamHandler
=
new
PumpStreamHandler
(
fileOutputStream
,
fileOutputStream
,
null
);
// command
...
...
@@ -75,12 +82,6 @@ public class ScriptUtil {
exec
.
setExitValues
(
null
);
exec
.
setStreamHandler
(
streamHandler
);
int
exitValue
=
exec
.
execute
(
commandline
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
/*Process process = Runtime.getRuntime().exec(cmdarray);
IOUtils.copy(process.getInputStream(), out);
IOUtils.copy(process.getErrorStream(), out);*/
}
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论