Chạy một ứng dụng Java như một dịch vụ trên Linux


128

Tôi đã viết một ứng dụng máy chủ Java chạy trên một giải pháp Linux được lưu trữ ảo tiêu chuẩn. Ứng dụng này chạy mọi lúc để nghe các kết nối ổ cắm và tạo các trình xử lý mới cho chúng. Đây là một triển khai phía máy chủ cho ứng dụng máy khách-máy chủ.

Cách tôi khởi động là bằng cách đưa nó vào tập lệnh khởi động RC.local của máy chủ. Tuy nhiên, một khi đã bắt đầu, tôi không biết cách truy cập nó để dừng nó và nếu tôi muốn cài đặt bản cập nhật, vì vậy tôi phải khởi động lại máy chủ để khởi động lại ứng dụng.

Trên PC Windows, đối với loại ứng dụng này, tôi có thể tạo một dịch vụ windows và sau đó tôi có thể dừng và khởi động nó theo ý muốn. Có bất cứ điều gì như vậy trên một hộp Linux để nếu tôi khởi động ứng dụng này, tôi có thể dừng nó và khởi động lại mà không cần khởi động lại hoàn toàn máy chủ.

Ứng dụng của tôi được gọi là WebServer.exe. Nó được khởi động khi máy chủ khởi động bằng cách đưa nó vào trong RC.local của tôi như sau:

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

Tôi là một chút của một noob tại Linux vì vậy bất kỳ ví dụ sẽ được đánh giá cao với bất kỳ bài viết. Tuy nhiên, tôi có SSH và truy cập FTP đầy đủ vào hộp để cài đặt mọi bản cập nhật cũng như quyền truy cập vào bảng điều khiển Plesk.

Câu trả lời:


239

Tôi đã viết một trình bao bọc đơn giản khác ở đây:

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

Bạn có thể làm theo hướng dẫn đầy đủ cho init.d tại đây và cho systemd (ubfox 16+) tại đây

Nếu bạn cần nhật ký đầu ra thay thế 2

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

dòng cho

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

@PbxMan cảm ơn vì điều này. Tôi có thể cho nó đi và xem làm thế nào chúng ta có được trên. Chúc mừng.
dreza

2
nhưng làm thế nào tôi có thể chạy tập tin này? Tôi phải đặt nó ở đâu?
Jack Daniel

3
@JackDaniel trên các distro dựa trên debian, như chính debian và ubfox, bạn có thể thêm tập lệnh đó vào /etc/init.d. Sau đó, bạn có thể gọi nó như thế này: /etc/init.d/MyService bắt đầu. Và bạn có thể làm cho nó bắt đầu tự động bằng cách chạy mặc định update-rc.d MyService.
Andre

1
@ ThorbjørnRavnAndersen Điều đó phụ thuộc vào chương trình java của bạn. Nếu bạn không thể giết chương trình java của mình, hãy xem stackoverflow.com/questions/2541597/ ,. Tôi sẽ xóa MyService-pid thay vì kill và có luồng deamon trong phần Java để kiểm tra xem nó có tồn tại không.
PbxMan

1
Tập tin ouput của jar sẽ ở đâu? Làm thế nào tôi có thể cấu hình tên của nó?
M. Schena

48

Một giải pháp đơn giản là tạo một tập lệnh start.sh chạy Java thông qua nohup và sau đó lưu trữ PID vào một tệp:

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

Sau đó, tập lệnh dừng của bạn stop.sh sẽ đọc PID từ tệp và hủy ứng dụng:

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

Tất nhiên tôi đã bỏ qua một số chi tiết, như kiểm tra xem liệu quy trình có tồn tại và loại bỏ pid.txtnếu bạn hoàn thành.


2
Câu hỏi: Không phải lệnh kill $ PID sẽ khiến quá trình bị hủy mà không kết thúc? Tôi đang viết một chương trình máy chủ có giao diện với cơ sở dữ liệu và tôi muốn tất cả các luồng hiện đang chạy hoàn thành trước khi chương trình thoát ra, để đảm bảo rằng chương trình không bị chết giữa một lần ghi vào DB hoặc một cái gì đó .
Scuba Steve

2
@ scuba-steve loại. kill sẽ gửi tín hiệu HẠN, nó sẽ gọi bất kỳ móc tắt máy nào đang hoạt động, vì vậy hãy sử dụng chúng để kết thúc quá trình của bạn một cách duyên dáng. Họ sẽ không thực thi nếu quá trình có tín hiệu tiêu diệt (nghĩa là giết -9). HĐH có thể làm gián đoạn móc tắt máy của bạn nếu chúng mất quá nhiều thời gian để hoàn thành, vì vậy hãy giữ chúng gọn gàng
rjohnston

34

Tập lệnh init dịch vụ Linux được lưu trữ vào /etc/init.d. Bạn có thể sao chép và tùy chỉnh /etc/init.d/skeletontập tin, sau đó gọi

service [yourservice] start|stop|restart

xem http://www.ralfebert.de/blog/java/debian_daemon/ . Nó dành cho Debian (vì vậy, Ubuntu cũng vậy) nhưng phù hợp với phân phối hơn.


Trông đầy hứa hẹn. Tôi sẽ xem xét kỹ hơn về điều này. chúc mừng
dreza

11

Có thể không phải là giải pháp dev-op tốt nhất, nhưng tốt cho việc sử dụng chung máy chủ cho một bữa tiệc lan hoặc tương tự.

Sử dụng screenđể chạy máy chủ của bạn và sau đó tách ra trước khi đăng xuất, điều này sẽ giữ cho quá trình chạy, sau đó bạn có thể đính kèm lại bất cứ lúc nào.

Quy trình làm việc:

Bắt đầu một màn hình: screen

Bắt đầu máy chủ của bạn: java -jar minecraft-server.jar

Tháo bằng cách nhấn : Ctl-a,d

Đính kèm lại: screen -r

Thêm thông tin tại đây: https://www.gnu.org/software/screen/manual/screen.html


7

Một lựa chọn khác, cũng khá phổ biến là Java Service Wrapper . Điều này cũng khá phổ biến xung quanh cộng đồng OSS.


Chúc mừng. Tôi đã thấy một số đề cập về điều đó. Sẽ xem xét kỹ hơn.
dreza

5

Nói đến ứng dụng Spring Boot là một Dịch vụ , tôi sẽ dùng systemdphiên bản này, vì đây là phiên bản dễ nhất, ít dài dòng nhất và được tích hợp tốt nhất vào các bản phân phối hiện đại (và cả những bản không hiện đại như CentOS 7.x).



4

Dưới đây là tập lệnh shell mẫu (đảm bảo bạn thay thế tên MATH bằng tên của ứng dụng của bạn):

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

Vì một số lý do, điều này luôn báo cáo dịch vụ đã được bắt đầu. Có vẻ như pgrep đang trả về 0 khi chạy từ bên trong tập lệnh, nhưng nếu tôi nhập lệnh pgrep theo cách thủ công thì nó sẽ trả về 1.
HomeIsWhereThePcIs

Lý do tại sao pgrep nghĩ rằng dịch vụ đang chạy là vì nó phát hiện "/ bin / sh / sbin / dịch vụ MATH bắt đầu" và "/ bin / bash /etc/init.d/MATH start" và trả về 0
HomeIsWhereThePcIs

3

Từ ứng dụng Spring Boot dưới dạng Dịch vụ , tôi có thể đề xuất supervisordứng dụng dựa trên Python . Xem câu hỏi tràn ngăn xếp đó để biết thêm thông tin. Nó thực sự đơn giản để thiết lập.


supervisordthật tuyệt vời, đối với những người không biết, nó cho phép các dịch vụ giám sát (phải foreground- không phải daemonized), sau đó nó sẽ tự động khởi động lại các dịch vụ (và có thể gửi email thông báo khi khởi động lại xảy ra thông qua các plugin)
Wired00

2

Các câu trả lời khác làm tốt công việc đưa ra các tập lệnh và thiết lập tùy chỉnh tùy thuộc vào nền tảng của bạn. Ngoài những thứ đó, đây là những chương trình trưởng thành, có mục đích đặc biệt mà tôi biết:

  • JSW từ TanukiSoftware
  • YAJSW là một bản sao mã nguồn mở từ trên. Nó được viết bằng Java và nó là một quy trình bảo mẫu quản lý quy trình con (mã của bạn) theo các cấu hình. Hoạt động trên windows / linux.
  • JSVC là một ứng dụng gốc. Đây cũng là một quy trình bảo mẫu, nhưng nó gọi ứng dụng con của bạn thông qua JNI, chứ không phải là một quy trình con.


1

Từ hướng dẫn tham khảo khởi động mùa xuân

Cài đặt như một dịch vụ init.d (Hệ thống V)

Đơn giản chỉ cần liên kết mềm jar để init.dhỗ trợ các tiêu chuẩn start, stop, restartstatuscác lệnh. Giả sử rằng bạn đã cài đặt ứng dụng Spring Boot trong / var / myapp, để cài đặt ứng dụng Spring Boot dưới dạng dịch vụ init.d chỉ cần tạo một liên kết tượng trưng:

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

Sau khi cài đặt, bạn có thể bắt đầu và dừng dịch vụ theo cách thông thường. Ví dụ: trên hệ thống dựa trên Debian:

$ service myapp start

tiền boaNếu ứng dụng của bạn không khởi động được, hãy kiểm tra tệp nhật ký được ghi nhận /var/log/<appname>.loglỗi.

Tiếp tục đọc để biết cách bảo mật một dịch vụ đã triển khai.

Sau khi thực hiện bằng văn bản, tôi phát hiện ra rằng dịch vụ của tôi không bắt đầu bằng thông báo lỗi này trong nhật ký: start-stop-daemon: tùy chọn không được nhận dạng - không đóng . Và tôi đã quản lý để sửa nó bằng cách tạo một tệp cấu hình /var/myapp/myapp.confvới nội dung sau

USE_START_STOP_DAEMON=false

1

Tôi có ứng dụng Netty java và tôi muốn chạy nó như một dịch vụ với systemd. Thật không may, ứng dụng dừng lại cho dù tôi đang sử dụng loại nào. Cuối cùng, tôi đã bắt đầu java trong màn hình. Dưới đây là các tập tin cấu hình:

dịch vụ

[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

khởi đầu

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

từ thời điểm đó bạn có thể sử dụng systemctl [start|stop|status] service.


1

Để chạy mã Java dưới dạng daemon (dịch vụ), bạn có thể viết sơ khai dựa trên JNI.

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

cho một mã mẫu dựa trên JNI. Trong trường hợp này, bạn daemonize mã đã được bắt đầu như Java và vòng lặp chính được thực thi trong C. Nhưng cũng có thể đặt vòng lặp chính, daemon, dịch vụ bên trong Java.

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

Hãy vui vẻ với JNI!


0

Tuy nhiên, một khi đã bắt đầu, tôi không biết làm thế nào để truy cập nó để ngăn chặn nó

Bạn có thể viết một tập lệnh dừng đơn giản greps cho quá trình java của bạn, trích xuất PID và gọi kill trên nó. Nó không lạ mắt, nhưng nó thẳng tiến. Một cái gì đó như thế có thể là sự giúp đỡ khi bắt đầu:

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

2
Tôi không giỏi về linux nhưng không pkill nameofprocesslàm điều tương tự?
Denys Séguret

0

Có thể tiến hành chiến tranh như một dịch vụ Linux và bạn có thể muốn buộc tệp pom.xml của mình trước khi đóng gói, vì một số bản phân phối có thể không nhận ra trong chế độ tự động. Để làm điều đó, hãy thêm thuộc tính sau bên trong plugin spring-boot-maven-plugin.

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

Tiếp theo, thiết lập init.d của bạn với:

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

và bạn sẽ có thể chạy

service myapp start|stop|restart

Có nhiều tùy chọn khác mà bạn có thể tìm thấy trong tài liệu Spring Boot , bao gồm cả dịch vụ Windows.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.