【问题标题】:Run a Java Application as a Service on Linux在 Linux 上将 Java 应用程序作为服务运行
【发布时间】: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 面板的访问权限。

【问题讨论】:

    标签: java linux service


    【解决方案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 程序,请查看 stackoverflow.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】:
          【解决方案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 serverJMX 与您的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 不会做同样的事情吗?
                      猜你喜欢
                      • 1970-01-01
                      • 2010-11-27
                      • 1970-01-01
                      • 2013-06-08
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-06-21
                      • 1970-01-01
                      相关资源
                      最近更新 更多