在 Linux 上将 Java 应用程序作为服务运行
Posted
技术标签:
【中文标题】在 Linux 上将 Java 应用程序作为服务运行【英文标题】:Run a Java Application as a Service on Linux 【发布时间】:2012-06-27 13:02:51 【问题描述】:我编写了一个在标准虚拟托管 Linux 解决方案上运行的 Java 服务器应用程序。应用程序一直在运行,侦听套接字连接并为它们创建新的处理程序。它是客户端-服务器应用程序的服务器端实现。
我启动它的方式是将它包含在服务器的启动 rc.local 脚本中。但是一旦启动,我不知道如何访问它来停止它以及是否要安装更新,所以我必须重新启动服务器才能重新启动应用程序。
在 Windows PC 上,对于这种类型的应用程序,我可能会创建一个 Windows 服务,然后我可以根据需要停止和启动它。在 Linux 机器上是否有类似的东西,所以如果我启动这个应用程序,我可以停止它并重新启动它,而无需完全重新启动服务器。
我的应用程序名为 WebServer.exe。它在服务器启动时通过将其包含在我的 rc.local 中来启动:
java -jar /var/www/vhosts/myweb.com/phpserv/WebServer.jar &
我在 Linux 上有点菜鸟,所以任何示例都会在任何帖子中得到赞赏。不过我确实有 SSH 和对盒子的完整 FTP 访问权限以安装任何更新以及对 Plesk 面板的访问权限。
【问题讨论】:
【参考方案1】:我在这里写了另一个简单的包装器:
#!/bin/sh
SERVICE_NAME=MyService
PATH_TO_JAR=/usr/local/MyProject/MyJar.jar
PID_PATH_NAME=/tmp/MyService-pid
case $1 in
start)
echo "Starting $SERVICE_NAME ..."
if [ ! -f $PID_PATH_NAME ]; then
nohup java -jar $PATH_TO_JAR /tmp 2>> /dev/null >> /dev/null &
echo $! > $PID_PATH_NAME
echo "$SERVICE_NAME started ..."
else
echo "$SERVICE_NAME is already running ..."
fi
;;
stop)
if [ -f $PID_PATH_NAME ]; then
PID=$(cat $PID_PATH_NAME);
echo "$SERVICE_NAME stoping ..."
kill $PID;
echo "$SERVICE_NAME stopped ..."
rm $PID_PATH_NAME
else
echo "$SERVICE_NAME is not running ..."
fi
;;
restart)
if [ -f $PID_PATH_NAME ]; then
PID=$(cat $PID_PATH_NAME);
echo "$SERVICE_NAME stopping ...";
kill $PID;
echo "$SERVICE_NAME stopped ...";
rm $PID_PATH_NAME
echo "$SERVICE_NAME starting ..."
nohup java -jar $PATH_TO_JAR /tmp 2>> /dev/null >> /dev/null &
echo $! > $PID_PATH_NAME
echo "$SERVICE_NAME started ..."
else
echo "$SERVICE_NAME is not running ..."
fi
;;
esac
您可以关注 init.d here 和 systemd (ubuntu 16+) here 的完整教程
如果你需要输出日志替换2
nohup java -jar $PATH_TO_JAR /tmp 2>> /dev/null >> /dev/null &
行
nohup java -jar $PATH_TO_JAR >> myService.out 2>&1&
【讨论】:
@PbxMan 谢谢。我可能会试一试,看看我们进展如何。干杯。 但是我怎样才能运行这个文件呢?我必须把它放在哪里? @JackDaniel 在基于 debian 的发行版上,如 debian 本身和 ubuntu,您可以将该脚本添加到 /etc/init.d。然后你可以像这样调用它:/etc/init.d/MyService start。您可以通过运行 update-rc.d MyService defaults 使其自动启动。 @ThorbjørnRavnAndersen 这将取决于您的 java 程序。如果您无法杀死您的 java 程序,请查看 ***.com/questions/2541597/…。我会删除 MyService-pid 而不是 kill 并在 Java 部分中有守护线程来检查它是否存在。 jar 的输出文件在哪里?如何配置它的名称?【参考方案2】:一个简单的解决方案是创建一个脚本 start.sh,通过 nohup 运行 Java,然后将 PID 存储到一个文件中:
nohup java -jar myapplication.jar > log.txt 2> errors.txt < /dev/null &
PID=$!
echo $PID > pid.txt
然后您的停止脚本 stop.sh 将从文件中读取 PID 并终止应用程序:
PID=$(cat pid.txt)
kill $PID
当然我遗漏了一些细节,比如检查进程是否存在,如果完成则删除pid.txt
。
【讨论】:
问题:kill $PID 命令不会导致进程没有完成就被杀死吗?我正在编写一个与数据库接口的服务器程序,我希望所有当前正在运行的线程在程序退出之前完成,以确保程序不会在写入数据库或其他东西的过程中死掉. @scuba-steve 有点像。 kill 将发送 TERM 信号,该信号将调用任何已到位的关闭挂钩,因此请使用它们来优雅地结束您的进程。如果进程收到终止信号(即 kill -9),它们将不会执行。如果它们需要太长时间才能完成,操作系统可能会中断您的关闭挂钩,因此请保持简洁【参考方案3】:Linux 服务初始化脚本存储在/etc/init.d
中。可以复制自定义/etc/init.d/skeleton
文件,然后调用
service [yourservice] start|stop|restart
见http://www.ralfebert.de/blog/java/debian_daemon/。它适用于 Debian(因此,Ubuntu 也是如此),但适合更多发行版。
【讨论】:
看起来很有希望。我将对此进行仔细研究。欢呼【参考方案4】:也许不是最好的开发运维解决方案,但适合将服务器用于局域网聚会或类似的一般用途。
使用screen
运行您的服务器,然后在注销前分离,这将保持进程运行,然后您可以随时重新连接。
工作流程:
启动屏幕:screen
启动你的服务器:java -jar minecraft-server.jar
按下分离:Ctl-a
, d
重新附加:screen -r
更多信息在这里:https://www.gnu.org/software/screen/manual/screen.html
【讨论】:
【参考方案5】:另一个也很流行的替代方法是Java Service Wrapper。这在 OSS 社区中也很流行。
【讨论】:
【参考方案6】:同样参考Spring Boot application as a Service,我会选择systemd
版本,因为它是最简单、最不冗长并且最好地集成到现代发行版(甚至是不太现代的发行版,如 CentOS 7.x )。
【讨论】:
【参考方案7】:最简单的方法是使用supervisord
。请在此处查看完整详细信息:http://supervisord.org/
更多信息:
https://askubuntu.com/questions/779830/running-an-executable-jar-file-when-the-system-starts/852485#852485
https://www.digitalocean.com/community/tutorials/how-to-install-and-manage-supervisor-on-ubuntu-and-debian-vps
【讨论】:
【参考方案8】:这是一个示例 shell 脚本(确保将 MATH 名称替换为应用程序的名称):
#!/bin/bash
### BEGIN INIT INFO
# Provides: MATH
# Required-Start: $java
# Required-Stop: $java
# Short-Description: Start and stop MATH service.
# Description: -
# Date-Creation: -
# Date-Last-Modification: -
# Author: -
### END INIT INFO
# Variables
PGREP=/usr/bin/pgrep
JAVA=/usr/bin/java
ZERO=0
# Start the MATH
start()
echo "Starting MATH..."
#Verify if the service is running
$PGREP -f MATH > /dev/null
VERIFIER=$?
if [ $ZERO = $VERIFIER ]
then
echo "The service is already running"
else
#Run the jar file MATH service
$JAVA -jar /opt/MATH/MATH.jar > /dev/null 2>&1 &
#sleep time before the service verification
sleep 10
#Verify if the service is running
$PGREP -f MATH > /dev/null
VERIFIER=$?
if [ $ZERO = $VERIFIER ]
then
echo "Service was successfully started"
else
echo "Failed to start service"
fi
fi
echo
# Stop the MATH
stop()
echo "Stopping MATH..."
#Verify if the service is running
$PGREP -f MATH > /dev/null
VERIFIER=$?
if [ $ZERO = $VERIFIER ]
then
#Kill the pid of java with the service name
kill -9 $($PGREP -f MATH)
#Sleep time before the service verification
sleep 10
#Verify if the service is running
$PGREP -f MATH > /dev/null
VERIFIER=$?
if [ $ZERO = $VERIFIER ]
then
echo "Failed to stop service"
else
echo "Service was successfully stopped"
fi
else
echo "The service is already stopped"
fi
echo
# Verify the status of MATH
status()
echo "Checking status of MATH..."
#Verify if the service is running
$PGREP -f MATH > /dev/null
VERIFIER=$?
if [ $ZERO = $VERIFIER ]
then
echo "Service is running"
else
echo "Service is stopped"
fi
echo
# Main logic
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart|reload)
stop
start
;;
*)
echo $"Usage: $0 start|stop|status|restart|reload"
exit 1
esac
exit 0
【讨论】:
由于某种原因,这总是报告服务已经启动。从脚本内部运行时,pgrep 似乎返回 0,但如果我手动输入 pgrep 命令,它会返回 1。 pgrep之所以认为服务正在运行是因为它检测到“/bin/sh /sbin/service MATH start”和“/bin/bash /etc/init.d/MATH start”和返回 0【参考方案9】:来自Spring Boot application as a Service,我可以推荐基于Python 的supervisord
应用程序。有关更多信息,请参阅该堆栈溢出问题。设置起来非常简单。
【讨论】:
supervisord
很棒,对于那些不知道的人来说,它允许监控服务(必须是foreground
- 不是daemonized
),然后它会自动重启服务(并且可以发送电子邮件通过插件发生重启时发出警报)【参考方案10】:
其他答案可以很好地根据您的平台提供自定义脚本和设置。除此之外,还有我所知道的成熟的专用程序:
TanukiSoftware 的 JSW YAJSW 是上面的开源克隆。它是用Java编写的,是一个根据配置管理子进程(你的代码)的nanny进程。适用于 windows / linux。 JSVC 是本机应用程序。它也是一个保姆进程,但它通过 JNI 调用您的子应用程序,而不是作为子进程。【讨论】:
【参考方案11】:您可以使用Thrift server 或JMX 与您的Java 服务进行通信。
【讨论】:
【参考方案12】:来自Spring Boot Reference Guide
作为 init.d 服务安装(系统 V)
只需将 jar 符号链接到
init.d
即可支持标准的start
、stop
、restart
和status
命令。 假设您在 /var/myapp 中安装了 Spring Boot 应用程序,要将 Spring Boot 应用程序安装为 init.d 服务,只需创建一个符号链接:$ sudo ln -s /var/myapp/myapp.jar /etc/init.d/myapp
安装后,您可以按常规方式启动和停止服务。例如,在基于 Debian 的系统上:
$ service myapp start
如果您的应用程序无法启动,请检查写入
/var/log/<appname>.log
的日志文件是否有错误。
Continue reading 了解如何保护已部署的服务。
按照所写的操作后,我发现我的服务无法启动,并在日志中显示此错误消息:start-stop-daemon: unrecognized option --no-close。我已经设法通过创建一个配置文件/var/myapp/myapp.conf
来修复它,其中包含以下内容
USE_START_STOP_DAEMON=false
【讨论】:
【参考方案13】:可以将战争作为 Linux 服务运行,并且您可能希望在打包之前强制输入 pom.xml 文件,因为某些发行版可能无法在自动模式下识别。为此,请在 spring-boot-maven-plugin 插件中添加以下属性。
<embeddedLaunchScriptProperties>
<mode>service</mode>
</embeddedLaunchScriptProperties>
接下来,使用以下命令设置您的 init.d:
ln -s myapp.war /etc/init.d/myapp
然后你就可以跑了
service myapp start|stop|restart
您可以在Spring Boot documentation 中找到许多其他选项,包括 Windows 服务。
【讨论】:
【参考方案14】:我有 Netty java 应用程序,我想通过 systemd 将它作为服务运行。不幸的是,无论我使用什么类型,应用程序都会停止。最后,我将 java start 包装在屏幕中。以下是配置文件:
服务
[Unit]
Description=Netty service
After=network.target
[Service]
User=user
Type=forking
WorkingDirectory=/home/user/app
ExecStart=/home/user/app/start.sh
TimeoutStopSec=10
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
开始
#!/bin/sh
/usr/bin/screen -L -dmS netty_app java -cp app.jar classPath
从那时起,您可以使用systemctl [start|stop|status] service
。
【讨论】:
【参考方案15】:要将 Java 代码作为守护程序(服务)运行,您可以编写基于 JNI 的存根。
http://jnicookbook.owsiak.org/recipe-no-022/
获取基于 JNI 的示例代码。在这种情况下,您将对作为 Java 启动的代码进行守护,并在 C 中执行主循环。但也可以将主、守护、服务循环放在 Java 中。
https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo029
享受 JNI 的乐趣!
【讨论】:
【参考方案16】:但是一旦开始我不知道如何访问它来停止它
您可以编写一个简单的停止脚本,对您的 java 进程进行 greps,提取 PID 并对其调用 kill。这不是花哨的,但它是直截了当的。 作为开始,这样的事情可能会有所帮助:
#!/bin/bash
PID = ps ax | grep "name of your app" | cut -d ' ' -f 1
kill $PID
【讨论】:
我不是很擅长 linux 但pkill nameofprocess
不会做同样的事情吗?以上是关于在 Linux 上将 Java 应用程序作为服务运行的主要内容,如果未能解决你的问题,请参考以下文章
无法在Windows上将SonarQube 5.3作为服务运行