在 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 即可支持标准的 startstoprestartstatus 命令。 假设您在 /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/&lt;appname&gt;.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 应用程序作为服务运行的主要内容,如果未能解决你的问题,请参考以下文章

如何在 z/OS 上将 java 作为作业步骤运行

当我在 android 应用程序上将位置作为服务运行时出错

无法在Windows上将SonarQube 5.3作为服务运行

在 Linux 上将 Crashpad 与 Qt 集成

在 OpenSuse 上将 Cassandra 作为服务运行

在 Windows Server 2008 上将 Trac 作为 Windows 服务安装