fix(script):优化脚本任务进程销毁逻辑与日志读写

- 修复脚本任务在异常情况下进程无法终止的问题
-优化脚本文件生成逻辑,使用FileTool工具类替代原生IO操作
- 改进脚本执行日志读写逻辑,提升磁盘IO效率
- 完善Process资源释放,确保执行完毕后正确销毁进程
- 增强脚本执行参数校验,避免空内容写入- 优化多语言提示信息,新增GLUE源码相关国际化配置
- 调整控制器入参校验逻辑,增加源码内容非空判断- 规范代码注释与异常处理流程,提升可读性与健壮性
pull/72/head
xuxueli 1 month ago
parent 48ee4aa712
commit 169263c9a7

@ -2585,10 +2585,11 @@ public void execute() {
- 11、【优化】调度组件日志完善提升边界情况下问题定位效率
- 12、【升级】升级多项maven依赖至较新版本如 netty、groovy、spring、spring-ai、dify 等;
- 14、【优化】任务回调失败日志读写磁盘逻辑优化解决极端情况下大文件读写内存问题
- 15、【ING】UI框架重构升级提升交互体验
- 16、【ING】调整资源加载逻辑移除不必要的拦截器逻辑提升页面加载效率
- 17、【ING】规范API交互协议通用响应结构体调整为Response
- 18、【ING】Http通讯组件升级基于接口代理方式重构
- 15、【修复】脚本任务process销毁逻辑优化解决风险情况下脚本进程无法终止问题
- 16、【ING】UI框架重构升级提升交互体验
- 17、【ING】调整资源加载逻辑移除不必要的拦截器逻辑提升页面加载效率
- 18、【ING】规范API交互协议通用响应结构体调整为Response
- 19、【ING】Http通讯组件升级基于接口代理方式重构
### TODO LIST

@ -8,6 +8,7 @@ import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.util.JobGroupPermissionUtil;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.tool.core.StringTool;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
@ -63,6 +64,9 @@ public class JobCodeController {
@RequestParam("glueRemark") String glueRemark) {
// valid
if (StringTool.isBlank(glueSource)) {
return ReturnT.ofFail( (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_source")) );
}
if (glueRemark==null) {
return ReturnT.ofFail( (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) );
}

@ -137,6 +137,7 @@ jobinfo_opt_run=Run Once
jobinfo_opt_run_tips=Please input the address for this trigger. Null will be obtained from the executor
jobinfo_opt_registryinfo=Registry Info
jobinfo_opt_next_time=Next trigger time
jobinfo_glue_source=GLUE Source
jobinfo_glue_remark=Resource Remark
jobinfo_glue_remark_limit=Resource Remark length is limited to 4~100
jobinfo_glue_rollback=Version Backtrack

@ -137,6 +137,7 @@ jobinfo_opt_run=执行一次
jobinfo_opt_run_tips=请输入本次执行的机器地址,为空则从执行器获取
jobinfo_opt_registryinfo=注册节点
jobinfo_opt_next_time=下次执行时间
jobinfo_glue_source=GLUE源码
jobinfo_glue_remark=源码备注
jobinfo_glue_remark_limit=源码备注长度限制为4~100
jobinfo_glue_rollback=版本回溯

@ -137,6 +137,7 @@ jobinfo_opt_run=執行一次
jobinfo_opt_run_tips=請輸入本次執行的機器地址,為空則從執行器獲取
jobinfo_opt_registryinfo=注冊節點
jobinfo_opt_next_time=下次執行時間
jobinfo_glue_source=GLUE源碼
jobinfo_glue_remark=源碼備註
jobinfo_glue_remark_limit=源碼備註長度限制為4~100
jobinfo_glue_rollback=版本回復

@ -29,9 +29,9 @@ public class ScriptJobHandler extends IJobHandler {
File glueSrcPath = new File(XxlJobFileAppender.getGlueSrcPath());
if (glueSrcPath.exists()) {
File[] glueSrcFileList = glueSrcPath.listFiles();
if (glueSrcFileList!=null && glueSrcFileList.length>0) {
if (glueSrcFileList != null) {
for (File glueSrcFileItem : glueSrcFileList) {
if (glueSrcFileItem.getName().startsWith(String.valueOf(jobId)+"_")) {
if (glueSrcFileItem.getName().startsWith(jobId +"_")) {
glueSrcFileItem.delete();
}
}
@ -47,6 +47,7 @@ public class ScriptJobHandler extends IJobHandler {
@Override
public void execute() throws Exception {
// valid
if (!glueType.isScript()) {
XxlJobHelper.handleFail("glueType["+ glueType +"] invalid.");
return;

@ -1,11 +1,11 @@
package com.xxl.job.core.util;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.tool.core.ArrayTool;
import com.xxl.tool.core.AssertTool;
import com.xxl.tool.io.FileTool;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
@ -22,16 +22,18 @@ public class ScriptUtil {
/**
* make script file
*
* @param scriptFileName
* @param content
* @throws IOException
* @param scriptFileName script file name
* @param scriptContent script content
* @throws IOException exception
*/
public static void markScriptFile(String scriptFileName, String content) throws IOException {
// make file, filePath/gluesource/666-123456789.py
FileOutputStream fileOutputStream = null;
public static void markScriptFile(String scriptFileName, String scriptContent) throws IOException {
// make file: filePath/gluesource/666-123456789.py
FileTool.writeString(scriptFileName, scriptContent);
/*FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(scriptFileName);
fileOutputStream.write(content.getBytes("UTF-8"));
fileOutputStream.write(scriptContent.getBytes("UTF-8"));
fileOutputStream.close();
} catch (Exception e) {
throw e;
@ -39,131 +41,133 @@ public class ScriptUtil {
if(fileOutputStream != null){
fileOutputStream.close();
}
}
}*/
}
/**
*
*
* @param command
* @param scriptFile
* @param logFile
* @param params
* @return
* @throws IOException
* @param command command
* @param scriptFile script file
* @param logFile log file
* @param params params
* @return exit code
* @throws IOException exception
*/
public static int execToFile(String command, String scriptFile, String logFile, String... params) throws IOException {
FileOutputStream fileOutputStream = null;
Thread inputThread = null;
Thread errThread = null;
Thread errorThread = null;
Process process = null;
try {
// file
// 1、build file OutputStream
fileOutputStream = new FileOutputStream(logFile, true);
// command
// 2、build command
List<String> cmdarray = new ArrayList<>();
cmdarray.add(command);
cmdarray.add(scriptFile);
if (params!=null && params.length>0) {
if (ArrayTool.isNotEmpty(params)) {
for (String param:params) {
cmdarray.add(param);
}
}
String[] cmdarrayFinal = cmdarray.toArray(new String[cmdarray.size()]);
String[] cmdarrayFinal = cmdarray.toArray(new String[0]);
// process-exec
final Process process = Runtime.getRuntime().exec(cmdarrayFinal);
// 3、processexec
process = Runtime.getRuntime().exec(cmdarrayFinal);
Process finalProcess = process;
// log-thread
// 4、read script log: inputStream + errStream
final FileOutputStream finalFileOutputStream = fileOutputStream;
inputThread = new Thread(new Runnable() {
@Override
public void run() {
try {
copy(process.getInputStream(), finalFileOutputStream, new byte[1024]);
} catch (IOException e) {
XxlJobHelper.log(e);
}
inputThread = new Thread(() -> {
try {
copy(finalProcess.getInputStream(), finalFileOutputStream, true, false);
} catch (IOException e) {
XxlJobHelper.log(e);
}
});
errThread = new Thread(new Runnable() {
@Override
public void run() {
try {
copy(process.getErrorStream(), finalFileOutputStream, new byte[1024]);
} catch (IOException e) {
XxlJobHelper.log(e);
}
errorThread = new Thread(() -> {
try {
// 数据流CopyInput自动关闭Output不处理
copy(finalProcess.getErrorStream(), finalFileOutputStream, true, false);
} catch (IOException e) {
XxlJobHelper.log(e);
}
});
inputThread.start();
errThread.start();
errorThread.start();
// process-wait
// 5、processwait for result
int exitValue = process.waitFor(); // exit code: 0=success, 1=error
// log-thread join
// 6、thread join, wait for log
inputThread.join();
errThread.join();
errorThread.join();
return exitValue;
} catch (Exception e) {
XxlJobHelper.log(e);
return -1;
} finally {
// 7、close file OutputStream
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
XxlJobHelper.log(e);
}
}
// 8、interrupt thread
if (inputThread != null && inputThread.isAlive()) {
inputThread.interrupt();
}
if (errThread != null && errThread.isAlive()) {
errThread.interrupt();
if (errorThread != null && errorThread.isAlive()) {
errorThread.interrupt();
}
// 9、process destroy
if (process != null) {
process.destroy();
}
}
}
/**
* CopyInputOutput
*
* @param inputStream
* @param outputStream
* @param buffer
* @return
* @throws IOException
*/
private static long copy(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws IOException {
private static final int BUFFER_SIZE = 1024 * 8;
private static int copy(InputStream input, OutputStream output, boolean closeInput, boolean closeOutput) throws IOException {
AssertTool.notNull(input, "No InputStream specified");
AssertTool.notNull(output, "No OutputStream specified");
try {
long total = 0;
for (;;) {
int res = inputStream.read(buffer);
if (res == -1) {
break;
}
if (res > 0) {
total += res;
if (outputStream != null) {
outputStream.write(buffer, 0, res);
}
}
int byteCount = 0;
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
byteCount += bytesRead;
}
outputStream.flush();
//out = null;
inputStream.close();
inputStream = null;
return total;
output.flush();
return byteCount;
} finally {
if (inputStream != null) {
inputStream.close();
if (closeInput) {
close(input);
}
if (closeOutput) {
close(output);
}
}
}
private static void close(Closeable closeable) {
if (closeable == null) {
return;
}
try {
closeable.close();
} catch (IOException ex) {
// ignore
}
}
/**
*

Loading…
Cancel
Save