在Linux系统上将Java应用程序设置为服务

138

我编写了一个Java服务器应用程序,运行在标准的虚拟主机Linux解决方案上。该应用程序一直运行以监听套接字连接并为它们创建新处理程序。它是客户端/服务器应用程序的服务器端实现。

我启动它的方式是将其包含在服务器的启动rc.local脚本中。但是,一旦启动,我不知道如何访问它停止它,如果我想安装更新,所以我必须重新启动服务器才能重新启动应用程序。

在Windows PC上,对于这种类型的应用程序,我可能会创建一个Windows服务,然后可以随意停止和启动它。在Linux盒子上是否有类似的东西,以便如果我启动此应用程序,我可以停止它并重新启动它,而无需完全重启服务器。

我的应用程序名为WebServer.exe。 我通过将其包含在rc.local中在服务器启动时启动它,如下所示:

java -jar /var/www/vhosts/myweb.com/phpserv/WebServer.jar &

我对Linux有些陌生,所以希望能够得到任何示例和帖子的帮助。但是我有SSH、完整的FTP访问权限可以安装任何更新,并且可以访问Plesk面板。

16个回答

244

我在这里写了另一个简单的包装器:

#!/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教程这里和systemd (ubuntu 16+)教程这里

如果需要输出日志,请将2替换掉。

nohup java -jar $PATH_TO_JAR /tmp 2>> /dev/null >> /dev/null &

排队等候

nohup java -jar $PATH_TO_JAR >> myService.out 2>&1&

@PbxMan 谢谢你。我可能会试一下,看看我们的进展如何。干杯。 - dreza
2
但是我该如何运行这个文件?我应该把它放在哪里? - Jack Daniel
3
在基于Debian的发行版(如Debian和Ubuntu)中,您可以将该脚本添加到/etc/init.d目录中。然后,您可以像这样调用它:/etc/init.d/MyService start。您还可以通过运行update-rc.d MyService defaults来使其自动启动。 - Andre
1
@ThorbjørnRavnAndersen 这将取决于您的Java程序。如果您无法终止Java程序,请查看https://dev59.com/QnE85IYBdhLWcg3w8IM4。我会删除MyService-pid而不是kill,并在Java部分中添加守护线程来检查其是否存在。 - PbxMan
1
输出文件将保存在哪里?我该如何配置它的名称? - M. Schena
显示剩余4条评论

51

一个简单的解决方案是创建一个名为 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。

3
问题:执行“kill $PID”命令会导致进程被强制结束而无法完成吗?我正在编写一个与数据库进行交互的服务器程序,我希望在程序退出之前所有当前运行的线程都能够完成,以确保程序不会在向数据库写入或其他操作中途崩溃。 - Scuba Steve
2
@scuba-steve 有点像。kill 命令会发送 TERM 信号,这将调用任何已经设置的关闭钩子,因此请使用它们来优雅地结束进程。如果进程收到 kill 信号(即 kill -9),则不会执行它们。如果关闭钩子需要太长时间才能完成,操作系统可能会中断它们,因此请保持简洁。 - rjohnston

34

Linux服务初始化脚本存储在/etc/init.d中。您可以复制并自定义/etc/init.d/skeleton文件,然后调用。

service [yourservice] start|stop|restart

请查看http://www.ralfebert.de/blog/java/debian_daemon/。它适用于Debian(因此也适用于Ubuntu),但更适合其他发行版。


看起来很有前途。我会仔细研究一下。谢谢。 - dreza

14

也许不是最好的开发和运维解决方案,但适用于服务器在局域网聚会或类似情况下的一般使用。

使用screen来运行您的服务器并在注销前分离它,这将保持进程运行,您随后可以在任何时候重新连接。

工作流程:

启动屏幕:screen

启动您的服务器:java -jar minecraft-server.jar

Ctl-ad分离

重新连接:screen -r

更多信息请参见:https://www.gnu.org/software/screen/manual/screen.html


7

5

谈到将Spring Boot应用程序作为服务,我建议使用systemd版本,因为它是最简单、最不冗长以及最好集成到现代发行版(包括不太现代的发行版如CentOS 7.x)中的。



4
这是一个示例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。 - HomeIsWhereThePcIs
pgrep认为服务正在运行的原因是它检测到"/bin/sh /sbin/service MATH start"和"/bin/bash /etc/init.d/MATH start"并返回0。 - HomeIsWhereThePcIs

3

Spring Boot应用程序作为服务中,我可以推荐基于Python的supervisord应用程序。有关更多信息,请参见该堆栈溢出问题。它非常简单易用。


supervisord非常棒,对于那些不知道的人来说,它允许监控服务(必须是foreground而不是daemonized),然后会自动重启服务(并可以通过插件发送电子邮件警报以通知重启发生)。 - wired00

2

其他回答给出了针对不同平台的自定义脚本和设置,除此之外,下面是我知道的成熟的、特定目的的程序:

  • TanukiSoftware的JSW
  • YAJSW是上述软件的开源克隆版本。它是用Java编写的,是一个管家进程,根据配置来管理子进程(您的代码)。可在Windows/Linux上运行。
  • JSVC是一种本地应用程序。它也是一个管家进程,但是通过JNI调用您的子应用程序,而不是作为子进程调用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接