Ejecute una aplicación Java como servicio en Linux

128

He escrito una aplicación de servidor Java que se ejecuta en una solución Linux alojada virtual estándar. La aplicación se ejecuta todo el tiempo escuchando conexiones de socket y creando nuevos controladores para ellas. Es una implementación del lado del servidor para una aplicación cliente-servidor.

La forma en que lo inicio es incluyéndolo en el script de inicio rc.local del servidor. Sin embargo, una vez iniciado, no sé cómo acceder para detenerlo y si deseo instalar una actualización, entonces tengo que reiniciar el servidor para reiniciar la aplicación.

En una PC con Windows, para este tipo de aplicación, podría crear un servicio de Windows y luego puedo detenerlo e iniciarlo como quiera. ¿Hay algo así en una caja de Linux para que si inicio esta aplicación pueda detenerla y reiniciarla sin hacer un reinicio completo del servidor?

Mi aplicación se llama WebServer.exe. Se inicia al iniciar el servidor incluyéndolo en mi rc.local como tal:

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

Soy un poco novato en Linux, por lo que cualquier ejemplo sería apreciado con cualquier publicación. Sin embargo, tengo SSH y acceso FTP completo a la caja para instalar cualquier actualización, así como acceso a un panel Plesk.

dreza
fuente

Respuestas:

239

Escribí otro contenedor simple aquí:

#!/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 

Puede seguir un tutorial completo para init.d aquí y para systemd (ubuntu 16+) aquí

Si necesita el registro de salida, reemplace el 2

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

líneas para

nohup java -jar $PATH_TO_JAR >> myService.out 2>&1&
PbxMan
fuente
@PbxMan gracias por esto. Podría intentarlo y ver cómo nos llevamos. Salud.
Dreza
2
pero ¿cómo puedo ejecutar este archivo? donde debo ponerlo?
Jack Daniel
3
@JackDaniel en distribuciones basadas en Debian, como Debian y Ubuntu, puede agregar ese script a /etc/init.d. Entonces puede invocarlo así: /etc/init.d/MyService start. Y puede hacer que se inicie automáticamente ejecutando update-rc.d MyService defaults.
Andre
1
@ ThorbjørnRavnAndersen Eso dependería de su programa Java. Si no puede matar su programa Java, consulte stackoverflow.com/questions/2541597/… . Eliminaría MyService-pid en lugar de kill y tendría un hilo deamon en la parte de Java que verifica si existe.
PbxMan
1
¿Dónde estará el archivo de salida del jar? ¿Cómo puedo configurar su nombre?
M. Schena
48

Una solución simple es crear un script start.sh que ejecute Java a través de nohup y luego almacene el PID en un archivo:

nohup java -jar myapplication.jar > log.txt 2> errors.txt < /dev/null &
PID=$!
echo $PID > pid.txt

Luego, su script de detención stop.sh leería el PID del archivo y cerraría la aplicación:

PID=$(cat pid.txt)
kill $PID

Por supuesto, he omitido algunos detalles, como verificar si el proceso existe y eliminarlo pid.txtsi ha terminado.

Wernsey
fuente
2
Pregunta: ¿El comando kill $ PID no provocaría que se matara el proceso sin finalizar? Estoy escribiendo un programa de servidor que interactúa con una base de datos, y me gustaría que todos los hilos actualmente en ejecución terminen antes de que el programa salga, para asegurar que el programa no muera en medio de una escritura en el DB o algo así. .
Scuba Steve
2
@ Scuba-Steve tipo de. kill enviará la señal TERM, que invocará los ganchos de apagado que estén en su lugar, así que úselos para finalizar su proceso con gracia. No se ejecutarán si el proceso recibe una señal de muerte (es decir, matar -9). El sistema operativo puede interrumpir sus ganchos de cierre si están tomando demasiado tiempo para completa, por lo que mantenerlos sucinta
rjohnston
34

El script de inicio del servicio de Linux se almacena en /etc/init.d. Puede copiar y personalizar el /etc/init.d/skeletonarchivo y luego llamar

service [yourservice] start|stop|restart

ver http://www.ralfebert.de/blog/java/debian_daemon/ . Es para Debian (entonces, Ubuntu también) pero se ajusta a una mayor distribución.

Arcadien
fuente
Parece prometedor. Voy a echar un vistazo más de cerca a esto. aplausos
dreza
11

Tal vez no sea la mejor solución de desarrollo de operaciones, pero es bueno para el uso general de un servidor para una fiesta de LAN o similar.

Use screenpara ejecutar su servidor y luego desconecte antes de cerrar sesión, esto mantendrá el proceso en ejecución, luego puede volver a adjuntarlo en cualquier momento.

Flujo de trabajo:

Comience una pantalla: screen

Inicia tu servidor: java -jar minecraft-server.jar

Detach pulsando: Ctl-a,d

Vuelva a colocar: screen -r

Más información aquí: https://www.gnu.org/software/screen/manual/screen.html

Peter Peterson
fuente
7

Otra alternativa, que también es bastante popular, es el Java Service Wrapper . Esto también es bastante popular en la comunidad OSS.

Carlspring
fuente
Salud. Había visto alguna mención de eso. Echaremos un vistazo más de cerca.
dreza
5

Refiriéndome también a la aplicación Spring Boot como Servicio , elegiría la systemdversión, ya que es la más fácil, menos detallada y mejor integrada en las distribuciones modernas (e incluso las no tan modernas como CentOS 7.x).

yglodt
fuente
4

Aquí hay un script de shell de muestra (asegúrese de reemplazar el nombre MATH con el nombre de su aplicación):

#!/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
Matheus Sant Anna de Oliveira
fuente
Por alguna razón, esto siempre informa que el servicio ya está iniciado. Parece que pgrep devuelve 0 cuando se ejecuta desde el interior del script, pero si ingreso el comando pgrep manualmente, devuelve 1.
HomeIsWhereThePcIs
La razón por la cual pgrep piensa que el servicio se está ejecutando es porque detecta "/ bin / sh / sbin / service MATH start" y "/ bin / bash /etc/init.d/MATH start" y devuelve 0
HomeIsWhereThePcIs
3

Desde la aplicación Spring Boot as a Service , puedo recomendar la supervisordaplicación basada en Python . Vea esa pregunta de desbordamiento de pila para obtener más información. Es realmente sencillo de configurar.

Poni del alba
fuente
supervisordes genial, para aquellos que no lo saben, permite servicios de monitoreo (que deben ser foreground- no daemonized), luego reiniciará automáticamente los servicios (y puede enviar alertas por correo electrónico cuando se reinicien a través de complementos)
wired00
2

Otras respuestas hacen un buen trabajo al proporcionar scripts y configuraciones personalizadas dependiendo de su plataforma. Además de esos, aquí están los programas maduros de propósito especial que conozco:

  • JSW de TanukiSoftware
  • YAJSW es un clon de código abierto de lo anterior. Está escrito en Java, y es un proceso de niñera que gestiona el proceso secundario (su código) de acuerdo con las configuraciones. Funciona en windows / linux.
  • JSVC es una aplicación nativa. También es un proceso de niñera, pero invoca su aplicación secundaria a través del JNI, en lugar de hacerlo como un subproceso.
Mori Bellamy
fuente
1

De Spring Boot Reference Guide

Instalación como un servicio init.d (Sistema V)

Simplemente enlazar simbólicamente el frasco para init.dapoyar la norma start, stop, restarty statuscomandos. Suponiendo que tiene una aplicación Spring Boot instalada en / var / myapp, para instalar una aplicación Spring Boot como un servicio init.d simplemente cree un enlace simbólico:

$ sudo ln -s /var/myapp/myapp.jar /etc/init.d/myapp

Una vez instalado, puede iniciar y detener el servicio de la forma habitual. Por ejemplo, en un sistema basado en Debian:

$ service myapp start

PropinaSi su aplicación no se inicia, revise el archivo de registro escrito para ver /var/log/<appname>.logsi hay errores.

Continúe leyendo para saber cómo asegurar un servicio implementado.

Después de hacer lo escrito, descubrí que mi servicio no puede iniciarse con este mensaje de error en los registros: start-stop-daemon: opción no reconocida --no-close . Y he logrado solucionarlo creando un archivo de configuración /var/myapp/myapp.confcon el siguiente contenido

USE_START_STOP_DAEMON=false
naXa
fuente
1

Tengo una aplicación Netty java y quiero ejecutarla como un servicio con systemd. Lamentablemente, la aplicación se detiene sin importar el tipo que esté usando. Al final he envuelto java start en pantalla. Aquí están los archivos de configuración:

Servicio

[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

comienzo

#!/bin/sh
/usr/bin/screen -L -dmS netty_app java -cp app.jar classPath

desde ese punto puedes usar systemctl [start|stop|status] service.

gskillz
fuente
0

Sin embargo, una vez comenzado, no sé cómo acceder para detenerlo

Puede escribir una secuencia de comandos de detención simple que haga greps para su proceso de Java, extraiga el PID y llame a kill en él. No es lujoso, pero es sencillo. Algo así puede ser de ayuda como comienzo:

#!/bin/bash
PID = ps ax | grep "name of your app" | cut -d ' ' -f 1
kill $PID
hovanessyan
fuente
2
No soy muy bueno en Linux, pero ¿no pkill nameofprocesshago lo mismo?
Denys Séguret
0

Es posible ejecutar la guerra como un servicio de Linux, y es posible que desee forzar su archivo pom.xml antes de empaquetarlo, ya que algunas distribuciones pueden no reconocerse en modo automático. Para hacerlo, agregue la siguiente propiedad dentro del complemento spring-boot-maven-plugin.

                    <embeddedLaunchScriptProperties>
                        <mode>service</mode>
                    </embeddedLaunchScriptProperties>

Luego, configure su init.d con:

ln -s myapp.war /etc/init.d/myapp

y podrás correr

service myapp start|stop|restart

Hay muchas otras opciones que puede encontrar en la documentación de Spring Boot , incluido el servicio de Windows.

Eder Luis Jorge
fuente