Exécuter une application Java en tant que service sous Linux

128

J'ai écrit une application serveur Java qui s'exécute sur une solution Linux hébergée virtuelle standard. L'application s'exécute tout le temps en écoutant les connexions de socket et en créant de nouveaux gestionnaires pour elles. Il s'agit d'une implémentation côté serveur vers une application client-serveur.

La façon dont je le démarre est de l'inclure dans le script de démarrage rc.local du serveur. Cependant une fois démarré je ne sais pas comment y accéder pour l'arrêter et si je veux installer une mise à jour, je dois donc redémarrer le serveur afin de redémarrer l'application.

Sur un PC Windows, pour ce type d'application, je peux créer un service Windows, puis je peux l'arrêter et le démarrer comme je le souhaite. Y a-t-il quelque chose comme ça sur une machine Linux pour que si je lance cette application, je puisse l'arrêter et la redémarrer sans faire un redémarrage complet du serveur.

Mon application s'appelle WebServer.exe. Il est lancé au démarrage du serveur en l'incluant dans mon rc.local en tant que tel:

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

Je suis un peu noob chez Linux, donc tout exemple serait apprécié avec tous les messages. Cependant, j'ai SSH et un accès FTP complet à la boîte pour installer les mises à jour ainsi que l'accès à un panneau Plesk.

Dreza
la source

Réponses:

239

J'ai écrit un autre wrapper simple ici:

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

Vous pouvez suivre un tutoriel complet pour init.d ici et pour systemd (ubuntu 16+) ici

Si vous avez besoin du journal de sortie, remplacez le 2

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

lignes pour

nohup java -jar $PATH_TO_JAR >> myService.out 2>&1&
PbxMan
la source
@PbxMan merci pour cela. Je pourrais essayer et voir comment nous nous en sortons. À votre santé.
dreza
2
mais comment puis-je exécuter ce fichier? où je dois le mettre?
Jack Daniel
3
@JackDaniel sur les distributions basées sur Debian, comme debian lui-même et ubuntu, vous pouvez ajouter ce script à /etc/init.d. Ensuite, vous pouvez l'invoquer comme ceci: /etc/init.d/MyService start. Et vous pouvez le faire démarrer automatiquement en exécutant les valeurs par défaut de update-rc.d MyService.
Andre
1
@ ThorbjørnRavnAndersen Cela dépendrait de votre programme java. Si vous ne pouvez pas tuer votre programme java Consultez stackoverflow.com/questions/2541597/… . Je supprimerais le MyService-pid au lieu du kill et aurais le fil de démon dans la partie Java qui vérifie s'il existe.
PbxMan
1
Où sera le fichier de sortie du pot? comment puis-je configurer son nom?
M. Schena
48

Une solution simple consiste à créer un script start.sh qui exécute Java via nohup, puis stocke le PID dans un fichier:

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

Ensuite, votre script d'arrêt stop.sh lirait le PID à partir du fichier et tuerait l'application:

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

Bien sûr, j'ai omis certains détails, comme vérifier si le processus existe et supprimer pid.txtsi vous avez terminé.

Wernesey
la source
2
Question: La commande kill $ PID ne ferait-elle pas tuer le processus sans terminer? J'écris un programme serveur qui s'interface avec une base de données, et je voudrais que tous les threads en cours d'exécution se terminent avant la fermeture du programme, afin de m'assurer que le programme ne meurt pas au milieu d'une écriture dans la base de données ou quelque chose. .
Scuba Steve
2
@ scuba-steve sorte de. kill enverra le signal TERM, qui invoquera tous les hooks d'arrêt en place, alors utilisez-les pour terminer votre processus en douceur. Ils ne s'exécuteront pas si le processus reçoit un signal d'arrêt (par exemple, kill -9). Le système d'exploitation peut interrompre vos hooks d'arrêt s'ils prennent trop de temps à terminer, alors gardez-les succincts
rjohnston
34

Le script d'initialisation du service Linux est stocké dans /etc/init.d. Vous pouvez copier et personnaliser le /etc/init.d/skeletonfichier, puis appeler

service [yourservice] start|stop|restart

voir http://www.ralfebert.de/blog/java/debian_daemon/ . C'est pour Debian (donc, Ubuntu aussi) mais convient à plus de distribution.

Arcadien
la source
Cela semble prometteur. Je vais examiner cela de plus près. cheers
dreza
11

Peut-être pas la meilleure solution dev-ops, mais bonne pour l'utilisation générale d'un serveur pour une partie LAN ou similaire.

Utilisez screenpour exécuter votre serveur puis détachez-le avant de vous déconnecter, cela maintiendra le processus en cours d'exécution, vous pouvez ensuite le reconnecter à tout moment.

Flux de travail:

Démarrez un écran: screen

Démarrez votre serveur: java -jar minecraft-server.jar

Détacher en appuyant sur: Ctl-a,d

Re-attacher: screen -r

Plus d'informations ici: https://www.gnu.org/software/screen/manual/screen.html

Peter Peterson
la source
7

Une autre alternative, qui est également très populaire, est le Java Service Wrapper . Ceci est également très populaire dans la communauté OSS.

carlspring
la source
À votre santé. J'en avais vu une mention. Regardera de plus près.
dreza
5

En ce qui concerne également l' application Spring Boot en tant que service , systemdj'opterais pour la version, car elle est la plus simple, la moins verbeuse et la mieux intégrée dans les distributions modernes (et même dans les distributions pas si modernes comme CentOS 7.x).

yglodt
la source
4

Voici un exemple de script shell (assurez-vous de remplacer le nom MATH par le nom de votre application):

#!/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
la source
Pour une raison quelconque, cela indique toujours que le service est déjà démarré. Il semble que pgrep renvoie 0 lorsqu'il est exécuté depuis l'intérieur du script, mais si j'entre la commande pgrep manuellement, il renvoie 1.
HomeIsWhereThePcIs
La raison pour laquelle pgrep pense que le service est en cours d'exécution est qu'il détecte "/ bin / sh / sbin / service MATH start" et "/ bin / bash /etc/init.d/MATH start" et renvoie 0
HomeIsWhereThePcIs
3

À partir de l' application Spring Boot en tant que service , je peux recommander l' supervisordapplication basée sur Python . Consultez cette question sur le débordement de pile pour plus d'informations. C'est vraiment simple à mettre en place.

Dawngerpony
la source
supervisordest génial, pour ceux qui ne le savent pas, cela permet de surveiller les services (ce qui doit être foreground- non daemonized), il redémarrera ensuite automatiquement les services (et peut envoyer des alertes par courrier électronique lorsque les redémarrages se produisent via des plugins)
câblé00
2

D'autres réponses font un bon travail en donnant des scripts et des configurations personnalisés en fonction de votre plate-forme. En plus de ceux-ci, voici les programmes matures et spéciaux que je connais:

  • JSW de TanukiSoftware
  • YAJSW est un clone open source de ce qui précède. Il est écrit en Java, et c'est un processus nounou qui gère le processus enfant (votre code) en fonction des configurations. Fonctionne sur Windows / Linux.
  • JSVC est une application native. C'est aussi un processus de nounou, mais il appelle votre application enfant via le JNI, plutôt que comme un sous-processus.
Mori Bellamy
la source
1

À partir du guide de référence Spring Boot

Installation en tant que service init.d (System V)

Simplement un lien symbolique le pot pour init.dsoutenir la norme start, stop, restartet les statuscommandes. En supposant que vous ayez une application Spring Boot installée dans / var / myapp, pour installer une application Spring Boot en tant que service init.d, créez simplement un lien symbolique:

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

Une fois installé, vous pouvez démarrer et arrêter le service de la manière habituelle. Par exemple, sur un système basé sur Debian:

$ service myapp start

PointeSi votre application ne démarre pas, /var/log/<appname>.logrecherchez les erreurs dans le fichier journal écrit .

continuer la lecture pour savoir comment sécuriser un service déployé.

Après avoir fait ce que j'ai écrit, j'ai découvert que mon service ne démarre pas avec ce message d'erreur dans les journaux: start-stop-daemon: option non reconnue --no-close . Et j'ai réussi à le réparer en créant un fichier de configuration /var/myapp/myapp.confavec le contenu suivant

USE_START_STOP_DAEMON=false
naXa
la source
1

J'ai l'application Netty java et je veux l'exécuter en tant que service avec systemd. Malheureusement, l'application s'arrête quel que soit le type que j'utilise. À la fin, j'ai enveloppé le début de java à l'écran. Voici les fichiers de configuration:

un service

[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

début

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

à partir de ce moment, vous pouvez utiliser systemctl [start|stop|status] service.

gskillz
la source
1

Pour exécuter du code Java en tant que démon (service), vous pouvez écrire un stub basé sur JNI.

http://jnicookbook.owsiak.org/recipe-no-022/

pour un exemple de code basé sur JNI. Dans ce cas, vous démonisez le code qui a été démarré en Java et la boucle principale est exécutée en C. Mais il est également possible de placer la boucle de service main, daemon, dans Java.

https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo029

Amusez-vous avec JNI!

Oo.oO
la source
0

Cependant une fois commencé je ne sais pas comment y accéder pour l'arrêter

Vous pouvez écrire un simple script d'arrêt qui greps pour votre processus java, extrait le PID et appelle kill dessus. Ce n'est pas chic, mais c'est simple. Quelque chose comme ça peut être utile pour commencer:

#!/bin/bash
PID = ps ax | grep "name of your app" | cut -d ' ' -f 1
kill $PID
hovanessyan
la source
2
Je ne suis pas très bon sous Linux mais pkill nameofprocessje ne fais pas la même chose?
Denys Séguret
0

Il est possible d'exécuter la guerre en tant que service Linux, et vous voudrez peut-être forcer votre fichier pom.xml avant l'empaquetage, car certaines distributions peuvent ne pas reconnaître en mode automatique. Pour ce faire, ajoutez la propriété suivante à l'intérieur du plugin spring-boot-maven-plugin.

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

Ensuite, configurez votre init.d avec:

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

et tu pourras courir

service myapp start|stop|restart

Il existe de nombreuses autres options que vous pouvez trouver dans la documentation de Spring Boot , y compris le service Windows.

Eder Luis Jorge
la source