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

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

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

@ -8,6 +8,7 @@ import com.xxl.job.admin.util.I18nUtil;
import com.xxl.job.admin.util.JobGroupPermissionUtil; import com.xxl.job.admin.util.JobGroupPermissionUtil;
import com.xxl.job.core.biz.model.ReturnT; import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.glue.GlueTypeEnum; import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.tool.core.StringTool;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
@ -63,6 +64,9 @@ public class JobCodeController {
@RequestParam("glueRemark") String glueRemark) { @RequestParam("glueRemark") String glueRemark) {
// valid // valid
if (StringTool.isBlank(glueSource)) {
return ReturnT.ofFail( (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_source")) );
}
if (glueRemark==null) { if (glueRemark==null) {
return ReturnT.ofFail( (I18nUtil.getString("system_please_input") + I18nUtil.getString("jobinfo_glue_remark")) ); 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_run_tips=Please input the address for this trigger. Null will be obtained from the executor
jobinfo_opt_registryinfo=Registry Info jobinfo_opt_registryinfo=Registry Info
jobinfo_opt_next_time=Next trigger time jobinfo_opt_next_time=Next trigger time
jobinfo_glue_source=GLUE Source
jobinfo_glue_remark=Resource Remark jobinfo_glue_remark=Resource Remark
jobinfo_glue_remark_limit=Resource Remark length is limited to 4~100 jobinfo_glue_remark_limit=Resource Remark length is limited to 4~100
jobinfo_glue_rollback=Version Backtrack jobinfo_glue_rollback=Version Backtrack

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

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

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

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