如何将Java程序守护化?
如何将Java程序守护化?
我有一个Java程序,我想在Linux系统上将其变为守护进程。换句话说,我希望在shell中启动它,并在我退出登录后继续运行。我还希望能够干净地停止程序。
我找到了这篇文章,它使用了一种结合了shell脚本和Java代码的方法来实现这一目标。看起来不错,但如果可能的话,我希望有更简单的方法。
你在Linux系统上守护化Java程序的首选方法是什么?
问题的出现的原因是作者经常需要运行一些Java程序,并且希望这些程序满足以下条件:1. 免受sighups的影响;2. 完全与启动它的shell断开连接;3. 生成一个包含stderr和stdout内容的日志文件,并将其显示出来;4. 允许作者在进行其他操作时停止查看正在进行的日志,而不会中断运行的进程。
解决方法是使用以下命令来启动Java程序:nohup java com.me.MyProgram </dev/null 2>&1 | tee logfile.log &
。这样的话,即使关闭了终端,Java程序仍然会继续运行,并且生成的日志文件内容可以通过查看logfile.log来查看。
然而,这种使用nohup
的方法是一个非常糟糕的主意,因为每次想要启动服务时都需要重新启动系统。
为了解决这个问题,可以使用守护进程(daemonize)的方式来启动Java程序。守护进程是在后台运行的进程,它与终端无关,并且不会受到sighups的影响。它可以随时启动和停止,而不需要重新启动系统。
下面是如何将Java程序守护化的步骤:
1. 创建一个脚本文件,用于启动Java程序。例如,可以创建一个名为start.sh的文件,并在其中添加以下内容:java com.me.MyProgram >> logfile.log &
。
2. 将脚本文件设置为可执行。可以使用chmod +x start.sh
命令来实现。
3. 使用nohup
命令和守护进程的方式来启动Java程序:nohup ./start.sh </dev/null 2>&1 &
。
现在,Java程序将以守护进程的方式运行,满足了作者所需的所有条件。
通过使用守护进程的方式启动Java程序,作者不再需要重新启动系统来启动服务,而只需要运行脚本文件即可。这样方便了作者的操作,并且避免了使用nohup
的坏处。
在使用Apache Commons Daemon将Java程序作为Linux守护进程或WinNT服务运行时,是否有一种方法能够识别系统或进程的停止和启动呢?
在实际开发中,我们经常需要将Java程序作为守护进程或服务运行,以便能够在后台持续运行,即使用户退出登录或系统重新启动。Apache Commons Daemon提供了一种方便的方式来实现这一点,但是它并没有提供直接的方法来识别系统或进程的停止和启动。
然而,我们可以通过其他方式来实现这一功能。以下是一种解决方法:
1. 使用Java的Runtime类来注册一个关闭钩子(shutdown hook)。关闭钩子是在JVM关闭之前执行的代码块,可以用来执行一些清理操作或通知其他进程。可以使用以下代码将关闭钩子添加到Java程序中:
Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { // 在这里编写关闭钩子的代码 } });
2. 在关闭钩子中,我们可以执行一些必要的操作,例如发送停止信号给其他进程或保存程序状态。具体的操作取决于你的需求。
通过以上的解决方法,我们可以在Java程序中识别系统或进程的停止和启动,并进行相应的处理。这样,我们就能更好地控制和管理我们的Java程序,确保其在后台持续运行,并在需要时能够正确地停止和启动。
如何将Java程序设置为守护进程?
问题的原因:如果您不能依赖于其他地方提到的Java Service Wrapper(例如,如果您在没有SW包的Ubuntu上运行),您可能希望采用传统的方法:让您的程序在 /var/run/$progname.pid 中写入其PID,并编写一个标准的SysV init脚本(例如,可以使用ntpd的脚本作为示例,它很简单)。最好还要符合LSB标准。
解决方法:首先,start函数会检查程序是否已经在运行(通过检查 /var/run/$progname.pid 是否存在,并且该文件的内容是正在运行进程的PID),如果没有则运行以下代码:
logfile=/var/log/$progname.log pidfile=/var/run/$progname.pid nohup java -Dpidfile=$pidfile $jopts $mainClass $logfile 2>&1
然后,stop函数会检查 /var/run/$progname.pid 文件,检查该文件是否为正在运行的进程的PID,并验证它是否为Java虚拟机(以防止杀死只是重用了来自已死Java守护进程的PID的进程)。然后杀死该进程。
当调用时,我的main()方法将首先将其PID写入System.getProperty("pidfile")定义的文件中。
然而,有一个主要障碍:在Java中,没有一种简单和标准的方法来获取JVM运行的进程的PID。下面是我想出的方法:
private static String getPid() { File proc_self = new File("/proc/self"); if(proc_self.exists()) try { return proc_self.getCanonicalFile().getName(); } catch(Exception e) { /// Continue on fall-back } File bash = new File("/bin/bash"); if(bash.exists()) { ProcessBuilder pb = new ProcessBuilder("/bin/bash","-c","echo $PPID"); try { Process p = pb.start(); BufferedReader rd = new BufferedReader(new InputStreamReader(p.getInputStream())); return rd.readLine(); } catch(IOException e) { return String.valueOf(Thread.currentThread().getId()); } } // This is a cop-out to return something when we don't have BASH return String.valueOf(Thread.currentThread().getId()); }
不需要Java写入PID,脚本也可以完成(echo $! > /var/run/my.pid)。此外,由于通常只有root用户可以写入 /var/run,您可以将shell脚本以root用户运行,将守护进程以其他用户身份运行。
除非您从shell中进行,否则无法更改Java的uid或euid,这就是为什么您必须使用sudo来完成的原因...如果您问我,这有点麻烦。如果您以运行的用户对 /var/run 和 /var/log 具有写入权限,那么更容易。
此外,Java编写PID文件/锁文件的理由是为了知道守护进程的初始化阶段是否成功完成(在该阶段末尾进行写入)。
需要注意的是,这个答案是无用的。您希望在创建套接字之后进行fork操作。这只是将进程与shell分离,这是完全不同的事情。
而且,这种情况下使用nohup是一个非常糟糕的主意。