Executar um aplicativo Java como um serviço no Linux

128

Eu escrevi um aplicativo de servidor Java que é executado em uma solução Linux hospedada virtual padrão. O aplicativo é executado o tempo todo ouvindo conexões de soquete e criando novos manipuladores para elas. É uma implementação do lado do servidor para um aplicativo cliente-servidor.

A maneira como inicio é incluindo-o no script de inicialização rc.local do servidor. No entanto, uma vez iniciado, não sei como acessá-lo para interrompê-lo e, se quiser instalar uma atualização, tenho que reiniciar o servidor para reiniciar o aplicativo.

Em um PC com Windows, para esse tipo de aplicativo, posso criar um serviço do Windows e depois parar e iniciá-lo como quiser. Existe algo parecido em uma caixa do Linux para que, se eu iniciar esse aplicativo, eu possa pará-lo e reiniciá-lo sem fazer uma reinicialização completa do servidor.

Meu aplicativo é chamado WebServer.exe. Ele é iniciado na inicialização do servidor, incluindo-o no meu rc.local, como tal:

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

Eu sou um pouco noob no Linux, então qualquer exemplo seria apreciado em qualquer post. No entanto, tenho SSH e acesso FTP completo à caixa para instalar as atualizações e o acesso a um painel do Plesk.

dreza
fonte

Respostas:

239

Eu escrevi outro invólucro simples aqui:

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

Você pode seguir um tutorial completo para o init.d aqui e para systemd (ubuntu 16+) aqui

Se você precisar do log de saída, substitua o 2

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

linhas para

nohup java -jar $PATH_TO_JAR >> myService.out 2>&1&
PbxMan
fonte
@PbxMan obrigado por isso. Eu posso tentar e ver como vamos. Felicidades.
dreza
2
mas como posso executar esse arquivo? onde devo colocá-lo?
Jack Daniel
3
@JackDaniel em distros baseadas no debian, como o próprio debian e o ubuntu, você pode adicionar esse script ao /etc/init.d. Em seguida, você pode invocá-lo assim: /etc/init.d/MyService start. E você pode fazê-lo iniciar automaticamente executando os padrões do MyService update-rc.d.
Andre Andre
1
@ ThorbjørnRavnAndersen Isso dependeria do seu programa java. Se você não pode matar o seu programa java, consulte Stackoverflow.com/questions/2541597/… . Eu excluiria o MyService-pid em vez do kill e teria um thread deamon na parte Java que verifica se ele existe.
PbxMan
1
Onde estará o arquivo de saída do jar? como posso configurar seu nome?
317 Schena
48

Uma solução simples é criar um script start.sh que execute Java através do nohup e armazene o PID em um arquivo:

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

Em seguida, seu script stop stop.sh lê o PID do arquivo e mata o aplicativo:

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

É claro que deixei de fora alguns detalhes, como verificar se o processo existe e remover pid.txtse você terminou.

Wernsey
fonte
2
Pergunta: O comando kill $ PID não faria com que o processo fosse morto sem concluir? Estou escrevendo um programa de servidor que faz interface com um banco de dados e gostaria que todos os threads atualmente em execução terminassem antes da saída do programa, para garantir que o programa não morra no meio de uma gravação no DB ou algo assim .
Mergulhador Steve
2
@ Scuba-Steve meio que. O kill enviará o sinal TERM, que invocará quaisquer ganchos de desligamento existentes, portanto, use-os para finalizar seu processo normalmente. Eles não serão executados se o processo receber um sinal de interrupção (ou seja, interrupção -9). O OS pode interromper seus ganchos de desligamento se eles estão demorando muito para terminar, assim mantê-los sucinta
rjohnston
34

O script init do serviço Linux é armazenado em /etc/init.d. Você pode copiar e personalizar /etc/init.d/skeletonarquivos e, em seguida, chamar

service [yourservice] start|stop|restart

consulte http://www.ralfebert.de/blog/java/debian_daemon/ . É para o Debian (também para o Ubuntu), mas cabe mais distribuição.

Arcadien
fonte
Parece promissor. Vou dar uma olhada mais de perto nisso. Cheers
dreza 26/06
11

Talvez não seja a melhor solução para dev-ops, mas boa para o uso geral de um servidor em uma LAN ou similar.

Use screenpara executar o servidor e desconectar antes de sair, isso manterá o processo em execução e você poderá reconectá-lo a qualquer momento.

Fluxo de trabalho:

Inicie uma tela: screen

Inicie seu servidor: java -jar minecraft-server.jar

Separar pressionando: Ctl-a,d

Anexar novamente: screen -r

Mais informações aqui: https://www.gnu.org/software/screen/manual/screen.html

Peter Peterson
fonte
7

Outra alternativa, que também é bastante popular, é o Java Service Wrapper . Isso também é bastante popular na comunidade OSS.

carlspring
fonte
Felicidades. Eu já tinha visto alguma menção disso. Vai dar uma olhada.
dreza
5

Referindo-me também ao aplicativo Spring Boot como um serviço , eu escolheria a systemdversão, já que é a mais fácil, menos detalhada e melhor integrada às distribuições modernas (e até as não tão modernas como o CentOS 7.x).

yglodt
fonte
4

Aqui está um script de shell de amostra (certifique-se de substituir o nome MATH pelo nome do seu aplicativo):

#!/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
fonte
Por alguma razão, esse serviço de relatórios sempre já foi iniciado. Parece que pgrep está retornando 0, quando executado a partir de dentro do script, mas se eu digitar o comando pgrep manualmente ele retorna 1.
HomeIsWhereThePcIs
A razão pela qual pgrep acha que o serviço está sendo executado é porque ele detecta "/ bin / sh / sbin / service MATH start" e "/ bin / bash /etc/init.d/MATH começar" e retorna 0
HomeIsWhereThePcIs
3

No aplicativo Spring Boot como um serviço , posso recomendar o supervisordaplicativo baseado em Python . Veja essa pergunta de estouro de pilha para obter mais informações. É realmente simples de configurar.

Dawngerpony
fonte
supervisord é ótimo, para quem não sabe, permite serviços de monitoramento (que devem ser foreground - não daemonized), ele irá, em seguida, serviços de reinício automático (e pode-mails quando reinicia ocorrer via plugins)
wired00
2

Outras respostas fazem um bom trabalho fornecendo scripts e configurações personalizadas, dependendo da sua plataforma. Além desses, aqui estão os programas maduros para fins especiais que eu conheço:

  • JSW da TanukiSoftware
  • YAJSW é um clone de código aberto acima. Está escrito em Java e é um processo de babá que gerencia o processo filho (seu código) de acordo com as configurações. Funciona no Windows / Linux.
  • JSVC é um aplicativo nativo. Também é um processo de babá, mas chama o aplicativo filho por meio do JNI, e não como um subprocesso.
Mori Bellamy
fonte
1

No Guia de Referência do Spring Boot

Instalação como um serviço init.d (Sistema V)

Basta ligar simbolicamente o frasco para init.dpara suportar o padrão start, stop, restarte statuscomandos. Supondo que você tenha um aplicativo Spring Boot instalado em / var / myapp, para instalar um aplicativo Spring Boot como um serviço init.d, basta criar um link simbólico:

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

Uma vez instalado, você pode iniciar e interromper o serviço da maneira usual. Por exemplo, em um sistema baseado no Debian:

$ service myapp start

DicaSe seu aplicativo falhar ao iniciar, verifique o arquivo de log gravado em /var/log/<appname>.logbusca de erros.

Continue lendo para saber como proteger um serviço implantado.

Depois de fazer o que foi escrito, descobri que meu serviço falha ao iniciar com esta mensagem de erro nos logs: opção start-stop-daemon: unrecognized --no-close . E eu consegui corrigi-lo criando um arquivo de configuração /var/myapp/myapp.confcom o seguinte conteúdo

USE_START_STOP_DAEMON=false
naXa
fonte
1

Estou tendo o aplicativo java Netty e quero executá-lo como um serviço com o systemd. Infelizmente, o aplicativo para independentemente do tipo que estou usando. No final, envolvi o java start na tela. Aqui estão os arquivos de configuração:

serviço

[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

começar

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

a partir desse ponto você pode usar systemctl [start|stop|status] service.

gskillz
fonte
1

Para executar o código Java como daemon (serviço), você pode escrever o stub baseado em JNI.

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

para um código de amostra baseado em JNI. Nesse caso, você daemoniza o código que foi iniciado como Java e o loop principal é executado em C. Mas também é possível colocar o loop de serviço principal, daemon dentro do Java.

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

Divirta-se com JNI!

Oo.oO
fonte
0

No entanto, uma vez iniciado, não sei como acessá-lo para pará-lo

Você pode escrever um script de parada simples que cumprimente seu processo java, extraia o PID e chame kill nele. Não é chique, mas é direto. Algo assim pode ser útil para começar:

#!/bin/bash
PID = ps ax | grep "name of your app" | cut -d ' ' -f 1
kill $PID
hovanessyan
fonte
2
Eu não sou muito bom em Linux, mas não pkill nameofprocessfaz a mesma coisa?
Denys Séguret
0

É possível executar a guerra como um serviço Linux, e você pode forçar seu arquivo pom.xml antes do empacotamento, pois algumas distribuições podem não ser reconhecidas no modo automático. Para fazer isso, adicione a seguinte propriedade dentro do plug-in spring-boot-maven-plugin.

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

Em seguida, configure seu init.d com:

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

e você poderá correr

service myapp start|stop|restart

Existem muitas outras opções que você pode encontrar na documentação do Spring Boot , incluindo o serviço Windows.

Eder Luis Jorge
fonte