import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import static java.nio.file.StandardWatchEventKinds.*;
public class WatchDog implements Launcher {
private static Logger LOGGER = LoggerFactory.getLogger(WatchDog.class);
public static final String ENV_KEY_TARGET = "WATCH_DOG_TARGET";
private final Launcher launcher;
public WatchDog(Launcher launcher) {
this.launcher = launcher;
}
@Override
public void launch(String[] args) {
if (!startWatchDog()) {
launcher.launch(args);
}
}
private static boolean startWatchDog() {
String target = System.getenv(ENV_KEY_TARGET);
if (null == target) {
LOGGER.debug("Do not have target for watch dog, launch directly");
return false;
}
LOGGER.debug("Watch dog is watching " + target + " ...");
watch(target);
return true;
}
private static void watch(String target) {
try {
int thisProcessId = Integer.valueOf(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]);
String thisProcessCommand = getProcessCommand(thisProcessId);
Process subProcess = launchWithoutWatchDog(thisProcessCommand);
final WatchService watchService = FileSystems.getDefault().newWatchService();
try {
Path targetPath = Paths.get(target);
Files.walkFileTree(targetPath, new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
dir.register(watchService, ENTRY_MODIFY, ENTRY_DELETE, ENTRY_CREATE);
return super.preVisitDirectory(dir, attrs);
}
});
while (true) {
WatchKey key = watchService.take();
key.pollEvents();
key.reset();
while (null != (key = watchService.poll())) {
key.pollEvents();
key.reset();
}
subProcess.destroy();
subProcess = launchWithoutWatchDog(thisProcessCommand);
}
} finally {
watchService.close();
}
} catch (Throwable e) {
throw new RuntimeException("Failed to watch " + target, e);
}
}
private static String getProcessCommand(int processId) {
String command = "pargs -l " + processId;
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
return readAll(process.getInputStream());
} catch (Throwable e) {
throw new RuntimeException("Failed to execute " + command, e);
}
}
private static String readAll(InputStream inputStream) throws IOException {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder result = new StringBuilder();
String line;
while (null != (line = reader.readLine())) {
result.append(line);
}
return result.toString();
} finally {
inputStream.close();
}
}
private static Process launchWithoutWatchDog(String command) {
try {
LOGGER.info("Launch: " + command);
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command(command.replace("'", "").split(" "));
processBuilder.environment().remove(ENV_KEY_TARGET);
processBuilder.inheritIO();
return processBuilder.start();
} catch (Throwable e) {
throw new RuntimeException("Failed to launch without watch dog: " + command, e);
}
}
}
public interface Launcher {
public void launch(String[] args);
}
五项关键技术:
- 代码改动的时候自动更新class,这个部分交由IDE来完成。
- 获取当前Java进程的启动命令行
- 获取当前进程的PID,由JMX的api完成
- 由PID获得命令行参数,这个部分由solaris的pargs完成,其他操作系统也有类似命令
- 探测文件夹内容的改动,由Java7的WatchServer完成
- 在Java进程内,启动另外一个Java进程。由Java7的ProcessBuilder完成。注意inheritIO这个太方便了。
- 同一个main函数,两种执行状态(监控和非监控)。由传入不同的环境变量完成。如果没有WATCH_DOG_TARGET那么就执行真正的main,如果有则把当前进程作为监控进程。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库