Uruchom aplikację Java jako usługę w systemie Linux

128

Napisałem aplikację serwerową Java, która działa na standardowym wirtualnym hostowanym rozwiązaniu Linux. Aplikacja działa cały czas nasłuchując połączeń gniazd i tworząc dla nich nowe programy obsługi. Jest to implementacja po stronie serwera do aplikacji klient-serwer.

Zacząłem od włączenia go do skryptu startowego rc.local serwera. Jednak po uruchomieniu nie wiem, jak uzyskać do niego dostęp, aby go zatrzymać i jeśli chcę zainstalować aktualizację, muszę ponownie uruchomić serwer, aby ponownie uruchomić aplikację.

Na komputerze z systemem Windows dla tego typu aplikacji mogę utworzyć usługę systemu Windows, a następnie mogę ją zatrzymać i uruchomić, jak chcę. Czy jest coś takiego na Linuksie, więc jeśli uruchomię tę aplikację, mogę ją zatrzymać i ponownie uruchomić bez wykonywania całkowitego restartu serwera.

Moja aplikacja nazywa się WebServer.exe. Jest uruchamiany przy starcie serwera, włączając go do mojego rc.local jako takiego:

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

Jestem trochę noobem w Linuksie, więc każdy przykład byłby doceniony w przypadku jakichkolwiek postów. Jednak mam SSH i pełny dostęp FTP do skrzynki, aby zainstalować aktualizacje, a także dostęp do panelu Plesk.

dreza
źródło

Odpowiedzi:

239

Napisałem tutaj kolejny prosty wrapper:

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

Możesz zapoznać się z pełnym samouczkiem dotyczącym init.d tutaj, a dla systemd (ubuntu 16+) tutaj

Jeśli potrzebujesz dziennika wyjściowego, zastąp plik 2

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

linie dla

nohup java -jar $PATH_TO_JAR >> myService.out 2>&1&
PbxMan
źródło
@PbxMan dzięki za to. Mogę spróbować i zobaczyć, jak sobie radzimy. Twoje zdrowie.
dreza
2
ale jak mogę uruchomić ten plik? gdzie mam to położyć?
Jack Daniel,
3
@JackDaniel w dystrybucjach opartych na Debianie, takich jak sam debian i ubuntu, możesz dodać ten skrypt do /etc/init.d. Następnie możesz wywołać go w ten sposób: /etc/init.d/MyService start. I możesz uruchomić go automatycznie, uruchamiając update-rc.d MyService defaults.
Andre,
1
@ ThorbjørnRavnAndersen To zależy od twojego programu java. Jeśli nie możesz zabić programu java, sprawdź stackoverflow.com/questions/2541597/… . Usunąłbym MyService-pid zamiast kill i miałbym wątek demona w części Java, który sprawdza, czy istnieje.
PbxMan
1
Gdzie będzie plik ouput pliku jar? jak mogę skonfigurować jego nazwę?
M. Schena
48

Prostym rozwiązaniem jest utworzenie skryptu start.sh, który uruchamia Javę przez nohup, a następnie zapisuje PID do pliku:

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

Wtedy twój skrypt zatrzymujący stop.sh odczytałby PID z pliku i zabił aplikację:

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

Oczywiście pominąłem kilka szczegółów, takich jak sprawdzenie, czy proces istnieje i usunięcie, pid.txtjeśli skończysz.

Wernsey
źródło
2
Pytanie: Czy polecenie kill $ PID nie spowodowałoby zabicia procesu bez jego zakończenia? Piszę program serwera, który łączy się z bazą danych i chciałbym, aby wszystkie aktualnie uruchomione wątki zakończyły się przed zakończeniem programu, aby upewnić się, że program nie umrze w środku zapisu do bazy danych lub coś w tym stylu .
Scuba Steve
2
@ scuba-steve w pewnym sensie. kill wyśle ​​sygnał TERM, który wywoła wszelkie haki zamykające, które są na miejscu, więc użyj ich, aby z wdziękiem zakończyć proces. Nie zostaną wykonane, jeśli proces otrzyma sygnał zabicia (tj. Kill -9). System operacyjny może przerwać haki
zamykające,
34

Skrypty startowe usługi Linux są przechowywane w plikach /etc/init.d. Możesz skopiować i dostosować /etc/init.d/skeletonplik, a następnie zadzwonić

service [yourservice] start|stop|restart

patrz http://www.ralfebert.de/blog/java/debian_daemon/ . Jest przeznaczony dla Debiana (więc również Ubuntu), ale pasuje do większej dystrybucji.

Arcadien
źródło
Wygląda obiecująco. Przyjrzę się temu bliżej. okrzyki
dreza
11

Może nie jest to najlepsze rozwiązanie dla deweloperów, ale dobre do ogólnego użytku serwera na imprezę LAN lub podobną.

Służy screendo uruchamiania serwera, a następnie odłączania go przed wylogowaniem. Dzięki temu proces będzie działał, możesz ponownie podłączyć w dowolnym momencie.

Przepływ pracy:

Uruchom ekran: screen

Uruchom swój serwer: java -jar minecraft-server.jar

Odłączyć naciskając: Ctl-a,d

Podłącz ponownie: screen -r

Więcej informacji tutaj: https://www.gnu.org/software/screen/manual/screen.html

Peter Peterson
źródło
7

Inną alternatywą, która jest również dość popularna, jest Java Service Wrapper . Jest to również dość popularne w społeczności OSS.

carlspring
źródło
Twoje zdrowie. Widziałem o tym wzmiankę. Przyjrzymy się bliżej.
dreza
5

Odnosząc się również do aplikacji Spring Boot jako usługi , wybrałbym systemdwersję, ponieważ jest to najłatwiejsza, najmniej szczegółowa i najlepiej zintegrowana z nowoczesnymi dystrybucjami (a nawet nie tak nowoczesnymi, jak CentOS 7.x).

yglodt
źródło
4

Oto przykładowy skrypt powłoki (pamiętaj, aby zastąpić nazwę MATEMATYCZNĄ nazwą aplikacji):

#!/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
MathOliveira
źródło
Z jakiegoś powodu zawsze informuje, że usługa jest już uruchomiona. Wygląda na to, że pgrep zwraca 0 po uruchomieniu z wnętrza skryptu, ale jeśli ręcznie wprowadzę polecenie pgrep, zwraca 1.
HomeIsWhereThePcIs
Powodem, dla którego pgrep uważa, że ​​usługa jest uruchomiona, jest to, że wykrywa "/ bin / sh / sbin / service MATH start" i "/ bin / bash /etc/init.d/MATH start" i zwraca 0
HomeIsWhereThePcIs
3

Z aplikacji Spring Boot as a Service mogę polecić supervisordaplikację opartą na Pythonie . Zobacz pytanie o przepełnienie stosu, aby uzyskać więcej informacji. Konfiguracja jest naprawdę prosta.

Dawngerpony
źródło
supervisordjest świetny, dla tych, którzy nie wiedzą, umożliwia monitorowanie usług (które muszą być foreground- nie daemonized), a następnie automatycznie restartuje usługi (i może wysyłać powiadomienia e-mail po ponownym uruchomieniu za pośrednictwem wtyczek)
wired00
2

Inne odpowiedzi wykonują dobrą robotę, dając niestandardowe skrypty i konfiguracje w zależności od platformy. Oprócz tych, oto dojrzałe programy specjalnego przeznaczenia, o których wiem:

  • JSW firmy TanukiSoftware
  • YAJSW jest klonem open source z powyższego. Jest napisany w Javie i jest procesem niani, który zarządza procesem potomnym (kodem) zgodnie z konfiguracjami. Działa na Windows / Linux.
  • JSVC to aplikacja natywna. Jest to również proces niania, ale wywołuje aplikację dziecka za pośrednictwem JNI, a nie jako podproces.
Mori Bellamy
źródło
1

Z przewodnika po rozruchu sprężynowym

Instalacja jako usługa init.d (System V)

Wystarczy podlinkowujemy słoik aby init.dwspierać standard start, stop, restarti statuspoleceń. Zakładając, że masz aplikację Spring Boot zainstalowaną w / var / myapp, aby zainstalować aplikację Spring Boot jako usługę init.d, po prostu utwórz dowiązanie symboliczne:

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

Po zainstalowaniu możesz uruchamiać i zatrzymywać usługę w zwykły sposób. Na przykład w systemie opartym na Debianie:

$ service myapp start

WskazówkaJeśli aplikacja nie uruchamia się, sprawdź, czy plik dziennika, w którym został zapisany, nie zawiera /var/log/<appname>.logbłędów.

Kontynuuj czytanie aby dowiedzieć się, jak zabezpieczyć wdrożoną usługę.

Po wykonaniu tego, co napisałem, odkryłem, że moja usługa nie uruchamia się i wyświetla ten komunikat o błędzie w dziennikach: start-stop-daemon: unrecognized opcja --no-close . I udało mi się to naprawić, tworząc plik konfiguracyjny /var/myapp/myapp.confz następującą zawartością

USE_START_STOP_DAEMON=false
naXa
źródło
1

Mam aplikację java Netty i chcę ją uruchomić jako usługę z systemd. Niestety aplikacja zatrzymuje się niezależnie od używanego typu. Na koniec zawinąłem start Java w screen. Oto pliki konfiguracyjne:

usługa

[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

początek

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

od tego momentu możesz użyć systemctl [start|stop|status] service.

gskillz
źródło
1

Aby uruchomić kod Java jako demon (usługa), możesz napisać kod pośredniczący oparty na JNI.

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

przykładowy kod oparty na JNI. W tym przypadku demonizujesz kod, który został uruchomiony jako Java, a główna pętla jest wykonywana w C. Ale możliwe jest również umieszczenie głównej pętli usługi w Javie.

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

Baw się z JNI!

Oo.oO
źródło
0

Jednak po uruchomieniu nie wiem, jak uzyskać do niego dostęp, aby go zatrzymać

Możesz napisać prosty skrypt zatrzymujący, który grepuje dla twojego procesu java, wyodrębnia PID i wywołuje na nim kill. To nie jest wyszukane, ale jest proste. Coś takiego może być pomocne na początek:

#!/bin/bash
PID = ps ax | grep "name of your app" | cut -d ' ' -f 1
kill $PID
hovanessyan
źródło
2
Nie jestem zbyt dobry w Linuksie, ale nie pkill nameofprocessrobię tego samego?
Denys Séguret
0

Możliwe jest prowadzenie wojny jako usługa Linuksa i możesz chcieć wymusić plik pom.xml przed pakowaniem, ponieważ niektóre dystrybucje mogą nie rozpoznawać w trybie automatycznym. Aby to zrobić, dodaj następującą właściwość wewnątrz wtyczki spring-boot-maven-plugin.

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

Następnie skonfiguruj swój init.d za pomocą:

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

i będziesz mógł biec

service myapp start|stop|restart

Istnieje wiele innych opcji, które można znaleźć w dokumentacji Spring Boot , w tym usługa Windows.

Eder Luis Jorge
źródło