fix 修复 windows DSL 模式:另一个程序正在使用此文件,进程无法访问。 问题

This commit is contained in:
bwcx_jzy 2022-12-01 09:10:25 +08:00
parent 84ddc27143
commit ccf2b66cb8
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
5 changed files with 229 additions and 162 deletions

View File

@ -5,16 +5,17 @@
### 🐣 新增功能
1. 【agent】新增 DSL 项目运行脚本环境变量配置(`run.start.scriptEnv`
2. 【agent】新增 DSL 项目自定义 `restart` 流程 `run.restart`
2. 【agent】新增 DSL 项目自定义 `restart` 流程 `run.restart`
### 🐞 解决BUG、优化功能
1. 【agent】修复 DSL 项目重启操作被偶发异常(自动)关闭问题
2. 【agent】优化 DSL 项目控制台日志输出格式
3. 优化日志监听器:控制台支持自动重定向、第一次启动项目自动重新 showlog
3. 【all】优化日志监听器:控制台支持自动重定向、第一次启动项目自动重新 showlog
4. 【server】节点超时时间配置为 0 失效问题
5. 【agent】修复进程列表在部分场景下进程号数字转换异常@易自玉)
6. 【agent】优化启动、停止、重启响应结果输出
7. 【agent】修复 windows DSL 模式:`另一个程序正在使用此文件,进程无法访问。` 问题
------

View File

@ -68,7 +68,6 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
@ -541,7 +540,7 @@ public abstract class AbstractProjectCommander {
.map(s -> {
int pid = ProjectCommanderUtil.parsePid(s);
return CommandOpResult.of(pid > 0, s);
}).orElse(null);
}).orElseGet(() -> CommandOpResult.of(false, STOP_TAG));
} else {
String tag = javaCopyItem == null ? nodeProjectInfoModel.getId() : javaCopyItem.getTagId();

View File

@ -25,16 +25,20 @@ package io.jpom.script;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.LineHandler;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.jiangzeyin.common.spring.SpringUtil;
import io.jpom.model.data.DslYmlDto;
import io.jpom.model.data.NodeProjectInfoModel;
import io.jpom.model.data.NodeScriptModel;
import io.jpom.service.script.NodeScriptServer;
import io.jpom.system.ConfigBean;
import io.jpom.system.ExtConfigBean;
import io.jpom.util.CommandUtil;
import lombok.Setter;
@ -59,6 +63,7 @@ public class DslScriptBuilder extends BaseRunScript implements Runnable {
private final String args;
private String action;
private File scriptFile;
private boolean autoDelete;
private Map<String, String> environment;
private DslScriptBuilder(String action, Map<String, String> environment, String args, String log) {
@ -166,6 +171,18 @@ public class DslScriptBuilder extends BaseRunScript implements Runnable {
}
@Override
public void close() {
super.close();
//
if (autoDelete) {
try {
FileUtil.del(this.scriptFile);
} catch (Exception ignored) {
}
}
}
/**
* 异步执行
*
@ -195,23 +212,33 @@ public class DslScriptBuilder extends BaseRunScript implements Runnable {
NodeScriptServer nodeScriptServer = SpringUtil.getBean(NodeScriptServer.class);
String scriptId = scriptProcess.getScriptId();
NodeScriptModel item = nodeScriptServer.getItem(scriptId);
Map<String, String> environment = DslScriptBuilder.environment(nodeProjectInfoModel);
File scriptFile;
boolean autoDelete = false;
if (item == null) {
scriptFile = FileUtil.file(nodeProjectInfoModel.allLib(), scriptId);
Assert.state(FileUtil.isFile(scriptFile), "脚本模版不存在:" + scriptProcess.getScriptId());
} else {
scriptFile = DslScriptBuilder.initScriptFile(item, nodeProjectInfoModel);
scriptFile = DslScriptBuilder.initScriptFile(item, environment);
autoDelete = true;
}
DslScriptBuilder builder = new DslScriptBuilder(action, scriptProcess.getScriptEnv(), scriptProcess.getScriptArgs(), log);
builder.putEnvironment(DslScriptBuilder.environment(nodeProjectInfoModel));
builder.putEnvironment(environment);
builder.setScriptFile(scriptFile);
builder.setAutoDelete(autoDelete);
return builder;
}
private static File initScriptFile(NodeScriptModel scriptModel, NodeProjectInfoModel nodeProjectInfoModel) {
String id = nodeProjectInfoModel.getId();
File scriptFile = scriptModel.scriptFile("_" + id);
Map<String, String> dslEnv = environment(nodeProjectInfoModel);
/**
* 创建脚本文件
*
* @param scriptModel 脚本对象
* @param dslEnv 环境变量
* @return file
*/
private static File initScriptFile(NodeScriptModel scriptModel, Map<String, String> dslEnv) {
String dataPath = ConfigBean.getInstance().getDataPath();
File scriptFile = FileUtil.file(dataPath, ConfigBean.SCRIPT_RUN_CACHE_DIRECTORY, StrUtil.format("{}.{}", IdUtil.fastSimpleUUID(), CommandUtil.SUFFIX));
// 替换内容
String context = scriptModel.getContext();
for (Map.Entry<String, String> envEntry : dslEnv.entrySet()) {
@ -222,6 +249,33 @@ public class DslScriptBuilder extends BaseRunScript implements Runnable {
return scriptFile;
}
/**
* 清理 脚本文件执行缓存
*/
public static void clearRunScript() {
String dataPath = ConfigBean.getInstance().getDataPath();
File scriptFile = FileUtil.file(dataPath, ConfigBean.SCRIPT_RUN_CACHE_DIRECTORY);
if (!FileUtil.isDirectory(scriptFile)) {
return;
}
File[] files = scriptFile.listFiles(pathname -> {
Date lastModifiedTime = FileUtil.lastModifiedTime(pathname);
DateTime now = DateTime.now();
long between = DateUtil.between(lastModifiedTime, now, DateUnit.HOUR);
// 文件大于一个小时才能被删除
return between > 1;
});
if (files == null) {
return;
}
for (File file : files) {
try {
FileUtil.del(file);
} catch (Exception ignored) {
}
}
}
private static Map<String, String> environment(NodeProjectInfoModel nodeProjectInfoModel) {
Map<String, String> dslEnv = new HashMap<>(10);
dslEnv.put("PROJECT_ID", nodeProjectInfoModel.getId());

View File

@ -35,6 +35,7 @@ import io.jpom.common.RemoteVersion;
import io.jpom.common.commander.AbstractProjectCommander;
import io.jpom.cron.CronUtils;
import io.jpom.model.data.NodeProjectInfoModel;
import io.jpom.script.DslScriptBuilder;
import io.jpom.service.manage.ProjectInfoService;
import io.jpom.system.AgentExtConfigBean;
import lombok.extern.slf4j.Slf4j;
@ -121,8 +122,16 @@ public class AutoBackLog {
private static void systemMonitor() {
// 开启检测调度
ThreadUtil.execute(() -> {
CronUtils.upsert("system_monitor", "0 0 0,12 * * ?", RemoteVersion::loadRemoteInfo);
RemoteVersion.loadRemoteInfo();
// 定时任务
CronUtils.upsert("system_monitor", "0 0 0,12 * * ?", AutoBackLog::systemTask);
systemTask();
});
}
private static void systemTask() {
// 启动加载
RemoteVersion.loadRemoteInfo();
// 清空脚本缓存
DslScriptBuilder.clearRunScript();
}
}

View File

@ -44,170 +44,174 @@ import java.io.File;
@Configuration
public class ConfigBean {
/**
* 用户名header
*/
public static final String JPOM_SERVER_USER_NAME = "Jpom-Server-UserName";
/**
* 用户名header
*/
public static final String JPOM_SERVER_USER_NAME = "Jpom-Server-UserName";
public static final String JPOM_AGENT_AUTHORIZE = "Jpom-Agent-Authorize";
public static final String JPOM_AGENT_AUTHORIZE = "Jpom-Agent-Authorize";
public static final String DATA = "data";
public static final String DATA = "data";
public static final int AUTHORIZE_ERROR = 900;
/**
* 脚本模板存放路径
*/
public static final String SCRIPT_DIRECTORY = "script";
/**
* 授权信息
*/
public static final String AUTHORIZE = "agent_authorize.json";
/**
*
*/
public static final String AUTHORIZE_USER_KEY = "jpom.authorize.agentName";
/**
*
*/
public static final String AUTHORIZE_AUTHORIZE_KEY = "jpom.authorize.agentPwd";
/**
* 程序升级信息文件
*/
public static final String UPGRADE = "upgrade.json";
/**
* 远程版本信息
*/
public static final String REMOTE_VERSION = "remote_version.json";
/**
* Jpom 程序运行的 application 标识
*/
@Value("${jpom.applicationTag:}")
public String applicationTag;
/**
* 程序端口
*/
@Value("${server.port}")
private int port;
/**
* 环境
*/
@Value("${" + ConfigFileApplicationListener.ACTIVE_PROFILES_PROPERTY + "}")
private String active;
/**
* 数据目录缓存大小
*/
private long dataSizeCache;
public static final int AUTHORIZE_ERROR = 900;
/**
* 脚本模板存放路径
*/
public static final String SCRIPT_DIRECTORY = "script";
/**
* 脚本默认运行缓存执行文件路径考虑 windows 文件被占用情况
*/
public static final String SCRIPT_RUN_CACHE_DIRECTORY = "script_run_cache";
/**
* 授权信息
*/
public static final String AUTHORIZE = "agent_authorize.json";
/**
*
*/
public static final String AUTHORIZE_USER_KEY = "jpom.authorize.agentName";
/**
*
*/
public static final String AUTHORIZE_AUTHORIZE_KEY = "jpom.authorize.agentPwd";
/**
* 程序升级信息文件
*/
public static final String UPGRADE = "upgrade.json";
/**
* 远程版本信息
*/
public static final String REMOTE_VERSION = "remote_version.json";
/**
* Jpom 程序运行的 application 标识
*/
@Value("${jpom.applicationTag:}")
public String applicationTag;
/**
* 程序端口
*/
@Value("${server.port}")
private int port;
/**
* 环境
*/
@Value("${" + ConfigFileApplicationListener.ACTIVE_PROFILES_PROPERTY + "}")
private String active;
/**
* 数据目录缓存大小
*/
private long dataSizeCache;
private volatile static ConfigBean configBean;
private volatile static ConfigBean configBean;
/**
* 单利模式
*
* @return config
*/
public static ConfigBean getInstance() {
if (configBean == null) {
synchronized (ConfigBean.class) {
if (configBean == null) {
configBean = SpringUtil.getBean(ConfigBean.class);
}
}
}
return configBean;
}
/**
* 单利模式
*
* @return config
*/
public static ConfigBean getInstance() {
if (configBean == null) {
synchronized (ConfigBean.class) {
if (configBean == null) {
configBean = SpringUtil.getBean(ConfigBean.class);
}
}
}
return configBean;
}
public int getPort() {
return port;
}
public int getPort() {
return port;
}
/**
* 获取项目运行数据存储文件夹路径
*
* @return 文件夹路径
*/
public String getDataPath() {
String dataPath = FileUtil.normalize(ExtConfigBean.getInstance().getPath() + StrUtil.SLASH + DATA);
FileUtil.mkdir(dataPath);
return dataPath;
}
/**
* 获取项目运行数据存储文件夹路径
*
* @return 文件夹路径
*/
public String getDataPath() {
String dataPath = FileUtil.normalize(ExtConfigBean.getInstance().getPath() + StrUtil.SLASH + DATA);
FileUtil.mkdir(dataPath);
return dataPath;
}
/**
* 获取pid文件
*
* @return file
*/
public File getPidFile() {
return new File(getDataPath(), StrUtil.format("pid.{}.{}",
JpomApplication.getAppType().name(), JpomManifest.getInstance().getPid()));
}
/**
* 获取pid文件
*
* @return file
*/
public File getPidFile() {
return new File(getDataPath(), StrUtil.format("pid.{}.{}",
JpomApplication.getAppType().name(), JpomManifest.getInstance().getPid()));
}
/**
* 获取当前项目全局 运行信息文件路径
*
* @param type 程序类型
* @return file
*/
public File getApplicationJpomInfo(Type type) {
return FileUtil.file(SystemUtil.getUserInfo().getTempDir(), "jpom", type.name());
}
/**
* 获取当前项目全局 运行信息文件路径
*
* @param type 程序类型
* @return file
*/
public File getApplicationJpomInfo(Type type) {
return FileUtil.file(SystemUtil.getUserInfo().getTempDir(), "jpom", type.name());
}
/**
* 获取 agent 端自动生成的授权文件路径
*
* @param dataPath 指定数据路径
* @return file
*/
public String getAgentAutoAuthorizeFile(String dataPath) {
return FileUtil.normalize(dataPath + StrUtil.SLASH + ConfigBean.AUTHORIZE);
}
/**
* 获取 agent 端自动生成的授权文件路径
*
* @param dataPath 指定数据路径
* @return file
*/
public String getAgentAutoAuthorizeFile(String dataPath) {
return FileUtil.normalize(dataPath + StrUtil.SLASH + ConfigBean.AUTHORIZE);
}
/**
* 是否为 pro 模式运行
*
* @return pro
*/
public boolean isPro() {
return StrUtil.equals(this.active, "pro");
}
/**
* 是否为 pro 模式运行
*
* @return pro
*/
public boolean isPro() {
return StrUtil.equals(this.active, "pro");
}
public String getActive() {
return active;
}
public String getActive() {
return active;
}
/**
* 获取临时文件存储路径
*
* @return file
*/
public File getTempPath() {
File file = new File(ConfigBean.getInstance().getDataPath());
file = FileUtil.file(file, "temp");
FileUtil.mkdir(file);
return file;
}
/**
* 获取临时文件存储路径
*
* @return file
*/
public File getTempPath() {
File file = new File(ConfigBean.getInstance().getDataPath());
file = FileUtil.file(file, "temp");
FileUtil.mkdir(file);
return file;
}
/**
* 数据目录大小
*
* @return byte
*/
public long dataSize() {
String dataPath = getDataPath();
long size = FileUtil.size(FileUtil.file(dataPath));
dataSizeCache = size;
return size;
}
/**
* 数据目录大小
*
* @return byte
*/
public long dataSize() {
String dataPath = getDataPath();
long size = FileUtil.size(FileUtil.file(dataPath));
dataSizeCache = size;
return size;
}
/**
* 获取脚本模板路径
*
* @return file
*/
public File getScriptPath() {
return FileUtil.file(this.getDataPath(), SCRIPT_DIRECTORY);
}
/**
* 获取脚本模板路径
*
* @return file
*/
public File getScriptPath() {
return FileUtil.file(this.getDataPath(), SCRIPT_DIRECTORY);
}
public long getDataSizeCache() {
return dataSizeCache;
}
public long getDataSizeCache() {
return dataSizeCache;
}
}