รัน Java Application เป็นบริการบน Linux


128

ฉันได้เขียนแอปพลิเคชันเซิร์ฟเวอร์ Java ที่ทำงานบนโซลูชัน Linux โฮสต์เสมือนมาตรฐาน แอปพลิเคชั่นทำงานตลอดเวลาเพื่อรับฟังการเชื่อมต่อซ็อกเก็ตและสร้างตัวจัดการใหม่สำหรับพวกเขา เป็นการใช้งานฝั่งเซิร์ฟเวอร์กับแอปพลิเคชันไคลเอนต์เซิร์ฟเวอร์

วิธีที่ฉันเริ่มต้นคือการรวมไว้ในสคริปต์เริ่มต้นrc.localของเซิร์ฟเวอร์ อย่างไรก็ตามเมื่อเริ่มต้นฉันไม่ทราบวิธีการเข้าถึงเพื่อหยุดมันและถ้าฉันต้องการติดตั้งการอัปเดตดังนั้นฉันต้องรีสตาร์ทเซิร์ฟเวอร์เพื่อเริ่มต้นแอปพลิเคชันใหม่

บน windows PC สำหรับแอปพลิเคชันประเภทนี้ฉันอาจสร้างบริการ windows จากนั้นฉันสามารถหยุดและเริ่มต้นได้ตามที่ต้องการ มีอะไรอย่างนั้นในกล่อง Linux ดังนั้นถ้าฉันเริ่มแอปพลิเคชันนี้ฉันสามารถหยุดและรีสตาร์ทได้โดยไม่ต้องรีสตาร์ทเซิร์ฟเวอร์อย่างสมบูรณ์

แอปพลิเคชันของฉันชื่อ WebServer.exe มันเริ่มต้นเมื่อเซิร์ฟเวอร์เริ่มทำงานโดยรวมไว้ในrc.localของฉันเช่น:

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

ฉันเป็นคนเล็กน้อยที่ Linux ดังนั้นตัวอย่างใด ๆ ที่จะได้รับการชื่นชมกับการโพสต์ใด ๆ อย่างไรก็ตามฉันมี SSH และการเข้าถึง FTP แบบเต็มไปยังกล่องเพื่อติดตั้งการปรับปรุงใด ๆ เช่นเดียวกับการเข้าถึงแผง Plesk

คำตอบ:


239

ฉันเขียน 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 

คุณสามารถติดตามแบบฝึกหัดเต็มรูปแบบสำหรับ 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 สำหรับ distros แบบเดเบียนเช่นเดเบียนเองและอูบุนตูคุณสามารถเพิ่มสคริปต์นั้นลงใน /etc/init.d จากนั้นคุณสามารถเรียกใช้ดังนี้: /etc/init.d/MyService start และคุณสามารถทำให้มันเริ่มโดยอัตโนมัติโดยเรียกใช้ update-rc.d ค่าเริ่มต้นของ MyService
อังเดร

1
@ ThorbjørnRavnAndersenนั่นจะขึ้นอยู่กับโปรแกรมภาษาจาวาของคุณ หากคุณไม่สามารถฆ่าโปรแกรม Java ของคุณตรวจสอบstackoverflow.com/questions/2541597/... ฉันจะลบ MyService-pid แทน kill และมีเธรด deamon ในส่วน Java ที่ตรวจสอบว่ามีอยู่หรือไม่
PbxMan

1
ไฟล์ ouput ของ jar จะเป็นอย่างไร ฉันจะกำหนดชื่อมันได้อย่างไร
M. Schena

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 จะไม่ทำให้กระบวนการถูกฆ่าโดยไม่เสร็จหรือไม่ ฉันกำลังเขียนโปรแกรมเซิร์ฟเวอร์ที่เชื่อมต่อกับฐานข้อมูลและฉันต้องการให้เธรดที่กำลังทำงานอยู่ทั้งหมดให้เสร็จก่อนที่โปรแกรมจะออกเพื่อให้แน่ใจว่าโปรแกรมไม่ตายในระหว่างการเขียนไปยัง DB หรือบางสิ่ง .
Scuba Steve

2
@ scuba-steve เรียงลำดับจาก kill จะส่งสัญญาณระยะซึ่งจะเรียกใช้ hooks การปิดใด ๆ ที่อยู่ในสถานที่ดังนั้นใช้พวกเขาเพื่อยุติกระบวนการของคุณอย่างสง่างาม พวกเขาจะไม่ดำเนินการหากกระบวนการได้รับสัญญาณการฆ่า (เช่น kill -9) ระบบปฏิบัติการอาจขัดจังหวะการปิดระบบของคุณหากใช้เวลาในการดำเนินการนานเกินไปดังนั้นให้กระชับ
rjohnston

34

ลินุกซ์สคริปต์ init /etc/init.dบริการจะถูกเก็บไว้ใน คุณสามารถคัดลอกและปรับแต่ง/etc/init.d/skeletonไฟล์แล้วโทร

service [yourservice] start|stop|restart

ดูhttp://www.ralfebert.de/blog/java/debian_daemon/ มันใช้สำหรับ Debian (เช่น Ubuntu ด้วย) แต่พอดีกับการกระจายที่มากขึ้น


ดูมีแนวโน้ม ฉันจะเข้าไปดูเรื่องนี้ให้ละเอียด ไชโย
เดรซา

11

อาจไม่ใช่โซลูชัน dev-ops ที่ดีที่สุด แต่ก็ดีสำหรับการใช้งานทั่วไปของเซิร์ฟเวอร์สำหรับปาร์ตี้แลนหรือคล้ายกัน

ใช้ screenเพื่อเรียกใช้เซิร์ฟเวอร์ของคุณแล้วแยกออกก่อนที่จะออกจากระบบซึ่งจะทำให้กระบวนการทำงานต่อไปคุณสามารถแนบใหม่อีกครั้งได้ทุกเมื่อ

ขั้นตอนการทำงาน:

เริ่มหน้าจอ: screen

เริ่มเซิร์ฟเวอร์ของคุณ: java -jar minecraft-server.jar

ถอดออกโดยกด: Ctl-a ,d

Re-แนบ: screen -r

ข้อมูลเพิ่มเติมที่นี่: https://www.gnu.org/software/screen/manual/screen.html


7

อีกทางเลือกหนึ่งซึ่งยังเป็นที่นิยมมากคือJava บริการ Wrapper สิ่งนี้ยังเป็นที่นิยมในชุมชน OSS


ไชโย ฉันเคยเห็นบางสิ่งที่กล่าวถึง จะมองใกล้ ๆ
dreza


4

supervisordวิธีที่ง่ายที่สุดคือการใช้งาน โปรดดูรายละเอียดทั้งหมดได้ที่นี่: http://supervisord.org/

ข้อมูลเพิ่มเติม:

/ubuntu/779830/running-an-executable-jar-file-when-the-system-starts/852485#852485

https://www.digitalocean.com/community/tutorials/how-to-install-and-manage-supervisor-on-ubuntu-and-debian-vps


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 เป็นบริการฉันสามารถแนะนำsupervisordแอพพลิเคชั่นที่ใช้Python ดูคำถามล้นสแต็คนั้นสำหรับข้อมูลเพิ่มเติม มันตั้งตรงไปตรงมาจริงๆ


supervisordดีมากสำหรับผู้ที่ไม่ทราบว่าสามารถใช้บริการตรวจสอบ (ซึ่งต้องforeground- ไม่ได้daemonized) จากนั้นจะเริ่มบริการอัตโนมัติใหม่ (และสามารถส่งอีเมลแจ้งเตือนเมื่อมีการรีสตาร์ทเกิดขึ้นผ่านปลั๊กอิน)
wired00

2

คำตอบอื่น ๆ ทำงานได้ดีโดยให้สคริปต์ที่กำหนดเองและการตั้งค่าตามแพลตฟอร์มของคุณ นอกเหนือจากที่กล่าวมานี่เป็นโปรแกรมสำหรับผู้ใหญ่ที่มีวัตถุประสงค์พิเศษที่ฉันรู้จัก:

  • JSW จาก TanukiSoftware
  • YAJSWเป็นโคลนโอเพนซอร์ซจากด้านบน มันเขียนใน Java และเป็นกระบวนการพี่เลี้ยงที่จัดการกระบวนการลูก (รหัสของคุณ) ตามการกำหนดค่า ทำงานบน windows / linux
  • JSVCเป็นแอปพลิเคชั่นดั้งเดิม มันยังเป็นกระบวนการพี่เลี้ยงด้วย แต่จะเรียกใช้แอปพลิเคชันลูกของคุณผ่าน JNI แทนที่จะเป็นกระบวนการย่อย


1

จากคู่มืออ้างอิงการบูตสปริง

การติดตั้งเป็นบริการ init.d (System V)

เพียง symlink ขวดที่จะinit.dให้การสนับสนุนมาตรฐานstart, stop, restartและstatusคำสั่ง สมมติว่าคุณมีแอพพลิเคชั่น Spring Boot ติดตั้งอยู่ใน / var / myapp เพื่อติดตั้งแอพพลิเคชั่น Spring Boot เป็นบริการ init.d เพียงแค่สร้าง symlink:

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

เมื่อติดตั้งแล้วคุณสามารถเริ่มและหยุดบริการได้ตามปกติ ตัวอย่างเช่นบนระบบที่ใช้ Debian:

$ service myapp start

ปลายหากแอปพลิเคชันของคุณไม่สามารถเริ่มการทำงานได้ให้ตรวจสอบไฟล์บันทึกที่เขียนขึ้นเพื่อ/var/log/<appname>.logหาข้อผิดพลาด

อ่านต่อเพื่อทราบวิธีรักษาความปลอดภัยของบริการที่ปรับใช้

หลังจากทำตามที่เขียนไว้ฉันได้ค้นพบว่าการให้บริการของฉันล้มเหลวในการเริ่มต้นด้วยข้อผิดพลาดนี้ในบันทึก: เริ่มต้นหยุดภูต: ตัวเลือกที่ไม่รู้จัก --no-ใกล้ และฉันจัดการแก้ไขด้วยการสร้างไฟล์กำหนดค่า/var/myapp/myapp.confด้วยเนื้อหาต่อไปนี้

USE_START_STOP_DAEMON=false

1

ฉันมีแอปพลิเคชัน Netty java และฉันต้องการเรียกใช้เป็นบริการที่มี systemd น่าเสียดายแอปพลิเคชันหยุดไม่ว่าฉันจะใช้ประเภทใด ในตอนท้ายฉันได้ห่อจาวาเริ่มบนหน้าจอ นี่คือไฟล์ปรับแต่ง:

บริการ

[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 เป็น daemon (บริการ) คุณสามารถเขียน JNI based stub

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

สำหรับโค้ดตัวอย่างที่ใช้ JNI ในกรณีนี้คุณ daemonize รหัสที่เริ่มต้นด้วย Java และ main loop ถูกเรียกใช้งานใน C แต่ก็เป็นไปได้ที่จะนำ main, daemon's, loop service ภายใน Java

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

ขอให้สนุกกับ JNI!


0

อย่างไรก็ตามเมื่อเริ่มฉันไม่รู้วิธีการเข้าถึงเพื่อหยุดมัน

คุณสามารถเขียนสคริปต์หยุดอย่างง่าย ๆ ที่ greps สำหรับกระบวนการจาวาของคุณแยก PID และการโทรฆ่ามัน มันไม่แฟนซี แต่มันตรงไปตรงมา บางสิ่งเช่นนี้อาจเป็นประโยชน์ในการเริ่มต้น:

#!/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 ของคุณก่อนการบรรจุเนื่องจาก distros บางตัวอาจไม่รู้จักในโหมดอัตโนมัติ เมื่อต้องการทำเพิ่มคุณสมบัติต่อไปนี้ภายในปลั๊กอิน 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.