如何在后台运行 Java 程序
1. 引言
有时候,我们希望在后台运行 Java 程序,即使终端关闭后程序也能继续运行。
2. 将 Java 作为后台进程运行
在后台运行 Java 程序的最简单方法之一是使用 shell 脚本中的 & 操作符:
#!/bin/sh
java -jar /web/server.jar &
echo $! > startupApp.pid
& 确保进程在后台运行,让我们可以在不等待进程完成的情况下继续使用终端。
$! > startupApp.pid 会捕获并存储最近执行的后台命令的 PID(进程 ID)。这个 PID 唯一标识正在运行的进程,可用于后续的进程管理任务,如监控或停止进程。
然而,这种方法有一个缺点。即使我们能继续使用终端,如果关闭终端,进程仍可能被终止。这会导致后台程序意外停止。
此外,由于进程未作为服务管理,很难对其进行有效控制。我们无法像使用 systemd 这样的正规服务管理器那样轻松查看日志或启动/停止进程。
3. 使用 Nohup 保持进程存活
如果我们不希望在关闭会话后进程结束,那么 nohup 就是解决方案。nohup(No Hang Up)可以让我们的进程在关闭会话或终端后继续运行。
以下是使用 nohup 的 shell 脚本:
nohup java -jar /web/server.jar > output.log 2>&1 &
echo $! > startupApp.pid
nohup 会确保进程在关闭终端会话后依然运行。> output.log 2>&1 部分帮助我们将标准输出和错误输出都重定向到一个名为 output.log 的文件中,方便后续查看日志(用于调试目的)。最后的 & 则让进程在后台运行。**
echo $! > startupApp.pid 将 PID 保存到 startupApp.pid 中,以供后续使用。
如果我们想终止进程,可以运行以下命令:
kill $(cat startupApp.pid)
4. 将 Java 作为 systemd 服务运行(适用于 Linux 服务器)
对于长期运行的 Java 进程以及更好的管理,我们需要使用 systemd 将它们作为服务来管理。如果我们使用的是 Linux 服务器,这是推荐的方法。现在让我们来看看如何实现这一点。
首先,我们需要使用以下命令创建一个.service 文件:
sudo nano /etc/systemd/system/myjavaapp.service
接下来,将以下内容添加到文件中:
[Unit]
Description: Java Background Service
After=network.target
[Service]
ExecStart=/usr/bin/java -jar /web/server.jar
WorkingDirectory=/web
Restart=always
User=root
StandardOutput=append:/var/log/myjavaapp.log
StandardError=append:/var/log/myjavaapp.err
[Install]
WantedBy=multi-user.target
我们正在尝试定义如何将 Java 应用程序作为后台服务运行。在这种情况下,我们要确保该应用在启动时自动运行,并且保持持续运行状态。
在 [Unit] 部分中,使用 After=network.target, 我们指定只有在网络可用后才启动。这一点非常重要,尤其是当我们的 Java 应用程序依赖于互联网或网络服务时。
接下来,在 [Service] 部分,我们定义了 Java 应用程序的运行方式。让我们逐一解析。
ExecStart 指示 systemd 通过运行 server.jar 来启动 Java 应用程序。WorkingDirectory 定义了服务将从哪个文件夹执行。在这里,它被设置为 /web 目录。
Restart 确保如果应用程序因任何原因崩溃,systemd 会自动重启该应用程序。
接下来,我们将用户属性设置为 root,这意味着服务将以 root 用户的身份运行。 然而,为了更高的安全性,通常建议以非 root 用户的身份运行服务。
我们还通过 StandOutput 和 StandardError 属性来跟踪日志和错误信息。这些属性对于分析与应用程序相关的问题非常有用。
最后,通过 WantedBy=multi-user.target 属性,我们让 Java 应用程序在系统达到服务正常运行阶段时自动启动,这样服务会在 GUI 渲染之前启动。如果没有这个配置,我们的服务将无法知道何时应该自动启动。它只会处于待机状态,每次重启后我们都必须手动启动它。
接下来,我们需要重新加载 systemd 并启用服务。每当我们创建或修改服务时,都需要这一步,让 systemd 了解这些变更。
我们需要运行以下命令,让 systemd 了解我们新添加(或修改)的服务:
sudo systemctl daemon-reload
接下来,我们需要创建一个指向服务文件的符号链接(快捷方式)。系统会将这个快捷方式放置在一个特殊的文件夹中,在启动时检查它,并在 multi-user.target 阶段启动服务。
以下是创建符号链接的命令:
sudo systemctl enable myjavaapp
现在,要启动 Java 应用程序,我们需要重启系统或者运行以下命令:
sudo systemctl start myjavaapp
我们可以使用以下命令来确认服务是否已启动:
sudo systemctl status myjavaapp
最后,要停止后台进程,我们可以运行以下命令:
sudo systemctl stop myjavaapp
5. 使用 screen 或 tmux 将 Java 作为后台进程运行
与 systemd 或 nohup 不同,screen 和 tmux 允许我们从会话中脱离(断开连接),而不会中断正在运行的进程,之后还能重新连接(重新附着),回到我们离开时的确切位置。
简单来说,使用 screen 或 tmux,我们可以启动一个 Java 应用程序,即使合上笔记本电脑或断开连接,之后回来时仍能看到同样的终端界面,就像什么都没发生过一样,而应用程序会一直在后台运行。
我们可以把它想象成 Windows 的休眠模式,只不过它仅限于终端会话。
我们来使用以下命令:
screen -S myjavaapp java -jar /web/server.jar
要进行分离操作,我们需要按下 Ctrl+A,松开这两个键,然后再按下 D。如果之后想重新连接,可以运行以下命令:
screen -r myjavaapp
我们可以使用 tmux 达到类似的效果。以下是相应的命令:
tmux new-session -s myjavaapp 'java -jar /web/server.jar'
要进行分离操作,我们需要按下 Ctrl+B,松开这两个键,然后按下 D。如果之后想重新连接,可以运行以下命令:
tmux attach-session -t myjavaapp
tmux 更加现代化,功能更强大,使用起来也更方便,而 screen 则简单易用,足以满足基本需求。不过,大多数 Linux 发行版都预装了 screen,而 tmux 可能需要我们单独安装。
6. 总结
在本文中,我们了解了如何将 Java 应用程序作为后台进程运行,探讨了在关闭终端会话后如何保持其运行,甚至还通过日志对其进行监控。
对于简单情况,nohup 或 & 可以起到作用,但在生产环境中,使用系统服务是一种更可靠的方法。像 screen 和 tmux 这样的工具为我们提供了额外的灵活性,即使在重启后也能继续进程,就像什么都没发生过一样。我们需要选择最适合自己使用场景的方法。