我有一个 java Spring REST API,带有一个使用 ProcessBuilder 类运行 Linux 命令的 Controller
。该命令是生成的“查找”命令
问题是,使用几天后,我发现托管服务器中有很多未终止的进程。我不知道为什么它们还在那里,没有结束或摧毁。 (我用 ps -ef 命令检查过)
这是我的 runCmd 函数:
public static final BufferedReader runCmd(String cmd) throws IOException, InterruptedException {
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("bash", "-c", cmd);
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
int ret = process.waitFor();
return output;
}
有没有办法确保没有留下更多进程?
更新
问题仅来自具有非常大的输出流(标准输出)的命令,感谢您的提示@DuncG
由于这个输出很重要,我不能忽视它。我必须找到一种方法来消耗它。
关于如何使用可运行线程来做到这一点有什么想法吗?
谢谢
最佳答案
您的命令是否会生成大量输出?僵尸的原因可能只是 cmd 向 STDOUT 写入了大量输出,并且流在 BufferedReader 中阻塞。
您可以通过将重定向添加到 null 来测试是否是这种情况 - 只需在 cmd 末尾附加“>/dev/null”即可。这会丢弃子进程的输出,并且意味着 BufferedReader 未充满未读数据/阻塞子进程。
processBuilder.command("bash", "-c", cmd + " > /dev/null");
如果这解决了僵尸问题,您可以恢复重定向并使 ProcessBuilder
将输出重定向到文件(在调用 start()
之前),否则您需要添加一个线程在 IO 生成时使用它。
Path tmpdir = Path.of(System.getProperty("java.io.tmpdir"));
Path out = tmpdir.resolve("stdout.log");
processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(out.toFile());
最后你应该返回输出文件供调用者检查,或者可以返回Files.newBufferedReader(out)
。
如果您不使用上面的重定向到文件,这将使用线程将输出捕获到内存缓冲区中。请注意,如果不重定向 ERR->OUT,您也需要复制 STDERR:
Process p = pb.start();
ByteArrayOutputStream stdout = new ByteArrayOutputStream(8192);
new Thread(() -> copy(p.getInputStream(), stdout), "STDOUT").start();
int rc = p.waitFor();
byte[] sour = stdout.toByteArray()
使用方法:
private static void copy(InputStream in, OutputStream buf)
{
try(var autoClose = in; var autoClose2 = buf)
{
in.transferTo(buf);
}
catch(IOException io)
{
throw new UncheckedIOException(io);
}
}
关于java - 使用 Java ProcessBuilder 未终止 Linux 僵尸进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63468663/