Запуск приложения Java как службы в Linux


128

Я написал серверное приложение Java, работающее на стандартном виртуальном хостинге Linux. Приложение все время работает, отслеживая соединения сокетов и создавая для них новые обработчики. Это серверная реализация клиент-серверного приложения.

Я начинаю это с включения в сценарий запуска rc.local сервера. Однако после запуска я не знаю, как получить к нему доступ, чтобы остановить его, и хочу ли я установить обновление, поэтому мне нужно перезапустить сервер, чтобы перезапустить приложение.

На ПК с Windows для этого типа приложений я могу создать службу Windows, а затем остановить и запустить ее по своему усмотрению. Есть ли что-нибудь подобное в коробке с Linux, чтобы, если я запустил это приложение, я мог остановить его и перезапустить без полного перезапуска сервера.

Мое приложение называется WebServer.exe. Он запускается при запуске сервера, включая его в мой rc.local как таковой:

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

Я немного новичок в Linux, поэтому любой пример будет оценен с любыми постами. Однако у меня есть SSH и полный FTP-доступ к серверу для установки любых обновлений, а также доступ к панели Plesk.

Ответы:


239

Я написал здесь еще одну простую оболочку:

#!/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 здесь и для systemd (ubuntu 16+) здесь

Если вам нужен выходной журнал, замените 2

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

линии для

nohup java -jar $PATH_TO_JAR >> myService.out 2>&1&

@PbxMan спасибо за это. Я могу попробовать и посмотреть, как у нас дела. Приветствия.
dreza

2
но как я могу запустить этот файл? где я должен это поставить?
Джек Дэниел

3
@JackDaniel в дистрибутивах на основе debian, таких как сам debian и ubuntu, вы можете добавить этот скрипт в /etc/init.d. Затем вы можете вызвать его так: /etc/init.d/MyService start. И вы можете заставить его запускаться автоматически, запустив update-rc.d MyService по умолчанию.
Андре

1
@ ThorbjørnRavnAndersen Это будет зависеть от вашей java-программы. Если вы не можете убить свою Java-программу, проверьте stackoverflow.com/questions/2541597/… . Я бы удалил MyService-pid вместо kill и имел поток deamon в части Java, который проверяет, существует ли он.
PbxMan

1
Где будет выходной файл баночки? как я могу настроить его имя?
M.

48

Простое решение - создать скрипт start.sh, который запускает Java через nohup, а затем сохраняет 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если вы закончили.


2
Вопрос: не приведет ли команда kill $ PID к завершению процесса без завершения? Я пишу серверную программу, которая взаимодействует с базой данных, и я бы хотел, чтобы все запущенные в данный момент потоки завершились до выхода из программы, чтобы гарантировать, что программа не умирает посреди записи в БД или чего-то еще. ,
Scuba Steve

2
@ scuba-steve вроде как. kill отправит сигнал TERM, который вызовет все имеющиеся ловушки завершения работы, поэтому используйте их для корректного завершения процесса. Они не будут выполняться, если процесс получит сигнал уничтожения (например, kill -9). ОС может прервать ваши перехватчики завершения работы, если они занимают слишком много времени для завершения, поэтому держите их краткими
rjohnston

34

Скрипт инициализации службы Linux хранится в файлах /etc/init.d. Вы можете скопировать и настроить /etc/init.d/skeletonфайл, а затем позвонить

service [yourservice] start|stop|restart

см. http://www.ralfebert.de/blog/java/debian_daemon/ . Это для Debian (как и для Ubuntu), но подходит для большего распространения.


Выглядит многообещающе. Я изучу это поближе. веселит
dreza

11

Может быть, это не лучшее решение для разработчиков, но хорошо для общего использования сервера для локальной сети или чего-то подобного.

Используйте screenдля запуска сервера, а затем отсоедините его перед выходом из системы, это будет поддерживать процесс, и вы можете повторно подключиться в любой момент.

Процедура:

Запустить экран: screen

Запустите свой сервер: java -jar minecraft-server.jar

Отделить нажатием: Ctl-a,d

Повторно приложить: screen -r

Подробнее здесь: https://www.gnu.org/software/screen/manual/screen.html


7

Другой альтернативой, которая также довольно популярна, является Java Service Wrapper . Это также довольно популярно в сообществе OSS.


Приветствия. Я видел некоторые упоминания об этом. Присмотримся внимательнее.
dreza

5

Ссылаясь также на приложение Spring Boot как услугу , я бы выбрал systemdверсию, поскольку это самый простой, наименее подробный и лучше всего интегрированный в современные дистрибутивы (и даже не такие современные, как CentOS 7.x).



4

Вот пример сценария оболочки (убедитесь, что вы заменили имя 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.
HomeIsWhereThePcIs

Причина, по которой pgrep считает, что служба запущена, заключается в том, что он обнаруживает «/ bin / sh / sbin / service MATH start» и «/ bin / bash /etc/init.d/MATH start» и возвращает 0
HomeIsWhereThePcIs

3

Из приложения Spring Boot as a Service я могу порекомендовать приложение на основе Python supervisord. См. Этот вопрос о переполнении стека для получения дополнительной информации. Это действительно просто настроить.


supervisordотлично, для тех, кто не знает, он позволяет контролировать службы (что должно быть foreground- нет daemonized), затем он автоматически перезапускает службы (и может
отправлять

2

Другие ответы хорошо справляются с предоставлением настраиваемых сценариев и настроек в зависимости от вашей платформы. В дополнение к этим, вот зрелые программы специального назначения, о которых я знаю:

  • JSW от TanukiSoftware
  • YAJSW - это клон с открытым исходным кодом из вышеперечисленного. Он написан на Java и представляет собой вспомогательный процесс, который управляет дочерним процессом (вашим кодом) в соответствии с конфигурациями. Работает на windows / linux.
  • JSVC - это собственное приложение. Это тоже няня-процесс, но он вызывает ваше дочернее приложение через JNI, а не как подпроцесс.


1

Из Справочного руководства по Spring Boot

Установка как служба init.d (System V)

Просто символическая ссылка банки , чтобы init.dподдерживать стандарт start, stop, restartи statusкоманду. Предполагая, что у вас установлено приложение Spring Boot в / var / myapp, для установки приложения Spring Boot в качестве службы init.d просто создайте символическую ссылку:

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

После установки вы можете запускать и останавливать службу обычным способом. Например, в системе на основе Debian:

$ service myapp start

НаконечникЕсли ваше приложение не запускается, проверьте записанный файл журнала на /var/log/<appname>.logналичие ошибок.

Продолжайте читать, чтобы узнать, как защитить развернутую службу.

После выполнения написанного я обнаружил, что моя служба не запускается с этим сообщением об ошибке в журналах: start-stop-daemon: unrecognized option --no-close . И мне удалось это исправить, создав файл конфигурации /var/myapp/myapp.confсо следующим содержимым

USE_START_STOP_DAEMON=false

1

У меня есть Java-приложение Netty, и я хочу запустить его как службу с помощью systemd. К сожалению, приложение останавливается независимо от того, какой тип я использую. В конце я завернул java start в screen. Вот файлы конфигурации:

служба

[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.


1

Для запуска кода Java в качестве демона (службы) вы можете написать заглушку на основе JNI.

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

для примера кода, основанного на JNI. В этом случае вы демонизируете код, который был запущен как Java, и основной цикл выполняется на C. Но также можно поместить основной, демонический, служебный цикл внутри Java.

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

Удачи с JNI!


0

Однако однажды начавшись, я не знаю, как получить к нему доступ, чтобы остановить его.

Вы можете написать простой сценарий остановки, который ищет ваш java-процесс, извлекает PID и вызывает для него kill. Это не причудливо, но все просто. Для начала может помочь что-то вроде этого:

#!/bin/bash
PID = ps ax | grep "name of your app" | cut -d ' ' -f 1
kill $PID

2
Я не очень хорошо разбираюсь в Linux, но не pkill nameofprocessумею делать то же самое?
Denys Séguret,

0

Можно запустить войну как службу 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 , включая службу Windows.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.