如何在后台运行 Java 程序?

1、简介

有时,我们需要在后台运行 Java 程序,即在 SSH 终端关闭后还能继续运行。

2、将 Java 作为后台进程运行

在后台运行 Java 程序的最简单方法之一是在 shell 脚本中使用 & 操作符:

#!/bin/sh
java -jar /web/server.jar &
echo $! > startupApp.pid

最后的 & 可确保进程在后台运行,而 echo $! > startupApp.pid 的作用是获取最后执行的后台命令的 PID(进程 ID),并且重定向输出到 startupApp.pid 文件中。该 PID 可唯一标识运行中的进程,以后可用于进程管理任务,如监控或停止进程。

不过,这种方法有一个缺点。如果我们断开了 SSH 会话,进程仍可能被终止,这会导致后台程序意外停止。

此外,由于进程不是作为服务来管理的,因此更难对其进行正确控制。我们无法像使用 systemd 等正规服务管理器那样轻松查看日志或启动/停止进程。

3、利用 Nohup 保活进程

如果想在断开 SSH 会话后还让后台进程继续执行,那么 nohup 就可以解决这个问题。nohup(不挂起)可以让进程在关闭会话或终端后继续运行。

nohup 用法如下:

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 -15 $(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 会自动重启应用程序。

接着,将 User 属性设置为 root,这意味着服务以 root 用户身份运行。不过,为了提高安全性,一般建议以非 root 用户身份运行服务。

还使用 StandOutputStandardError 属性跟踪日志和错误。这些有助于分析与应用程序相关的问题。

最后,通过 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 作为后台进程运行

systemdnohup 不同,screentmux 允许我们分离(断开)会话而不会停止运行的进程,并在稍后重新连接到我们离开的位置。

简单地说,使用 screentmux,我们可以启动 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 应用作为后台进程运行,以及如何实现在关闭 SSH 终端会话后进程不会终止。

对于简单的情况,nohup& 是非常方便的,但对于生产环境,推荐使用更可靠的 systemdscreentmux 等工具为提供了额外的灵活性,即使重启后也能继续执行进程,就像什么都没发生过一样。


Ref:https://www.baeldung.com/java-program-background