我有一个用 Java 编写的服务器,它作为 Windows 服务运行(感谢 Install4J)。我希望这个服务能够下载它运行的 JAR 文件的最新版本,并开始运行新代码。针是我不希望 Windows 服务完全退出。
理想情况下,我会通过 unix 风格的 exec() 调用来停止当前版本并运行新版本来完成此操作。我怎样才能最好地做到这一点?
我有一个用 Java 编写的服务器,它作为 Windows 服务运行(感谢 Install4J)。我希望这个服务能够下载它运行的 JAR 文件的最新版本,并开始运行新代码。针是我不希望 Windows 服务完全退出。
理想情况下,我会通过 unix 风格的 exec() 调用来停止当前版本并运行新版本来完成此操作。我怎样才能最好地做到这一点?
据我所知,在 Java 中没有办法做到这一点。
我想您可以通过使用 JavaRuntime.exec
或ProcessBuilder
start() 命令(启动新进程)然后让当前进程结束来解决它……文档状态
当没有更多对 Process 对象的引用时,子进程不会被终止,而是子进程继续异步执行。
我假设如果父级完成并且被垃圾收集也是如此。
问题是 Runtime.exec 的进程将不再有有效的输入、输出和错误流。
这是一种复杂但可移植的方式。
将您的代码分成两个罐子。一个非常小的罐子只是用来管理进程启动。它创建一个 ClassLoader,在其类路径中保存另一个 jar。
当你想加载一个新版本时,你终止所有从旧 jar 运行代码的线程。从旧 jar 中删除对类实例的所有引用。清空对加载旧 jar 的 ClassLoader 的所有引用。此时,如果您没有遗漏任何内容,那么旧的类和 ClassLoader 应该可以进行垃圾回收了。
现在,您从一个指向新 jar 的新 ClassLoader 实例重新开始,并重新启动您的应用程序代码。
Java Service Wrapper可以做到这一点以及更多。
您可以使用内置的重新加载功能,例如 Tomcat、Jetty 或 JBoss,它们可以作为服务运行,您不必将它们用作 Web 容器或 Java EE 容器。
其他选项是 OSGi,以及您自己的类加载功能。
但请注意在生产环境中重新加载。它并不总是最好的解决方案。
一般的做法:
import java.io.BufferedInputStream;
import java.util.Arrays;
public class ProcessSpawner {
public static void main(String[] args) {
//You can change env variables and working directory, and
//have better control over arguments.
//See [ProcessBuilder javadocs][1]
ProcessBuilder builder = new ProcessBuilder("ls", "-l");
try {
Process p = builder.start();
//here we just echo stdout from the process to java's stdout.
//of course, this might not be what you're after.
BufferedInputStream stream =
new BufferedInputStream(p.getInputStream());
byte[] b = new byte[80];
while(stream.available() > 0) {
stream.read(b);
String s = new String(b);
System.out.print(s);
Arrays.fill(b, (byte)0);
}
//exit with the exit code of the spawned process.
System.exit(p.waitFor());
} catch(Exception e) {
System.err.println("Exception: "+e.getMessage());
System.exit(1);
}
}
}