跳至主要內容

守护进程

chanchaw大约 7 分钟ubuntu

概述

一次为云服务器上的 FineReport 制作守护进程时使用 systemd 出现问题,启动命令执行要等很长时间,结果是失败的,后来使用 rc.local 就可以快速成功的制作开机启动。不过 ubuntu22server 开始就已经弃用 rc.local 了,需要手动制作该文件并启用

# 制作文件
sudo vim /etc/rc.local

# 黏贴如下代码,注意第一行的感叹号后面不要有空格,否则报错
# ==== 代码区域 start====
#!/bin/bash
sleep 10
/home/chanchaw/zipkin/start.sh

# 把所有自定义启动项都写在这一行的上面
# 保证本行是最后一行
exit 0
# ==== 代码区域 end ====

# 赋予可执行权限
sudo chmod +x /etc/rc.local

# 启用后台服务
sudo systemctl enable rc-local.service

# 手动启动服务
sudo systemctl enable rc-local.service

systemd 不好用

FineReport 制作守护进程,创建文件 fine-report.service

不可行!!!
# 创建文件
sudo vim /etc/systemd/system/fine-report.service

# 黏贴下面内容
[Unit]
Description=Fine Report Server
After=network.target
Wants=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/projs/barrel/report/server
ExecStart=/usr/lib/jvm/java8/bin/java -Xms512m -Xmx512m -jar fine-report.jar --spring.config.location=application.yml
ExecStop=/bin/kill -15 $MAINPID
SuccessExitStatus=143
Restart=always
RestartSec=10

# 设置JAVA_HOME环境变量
Environment="JAVA_HOME=/usr/lib/jvm/java8"
Environment="SPRING_CONFIG_LOCATION=./application.yml"

# 安全设置
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/projs/barrel/report/server

[Install]
WantedBy=multi-user.target



# 刷新守护进程
sudo systemctl daemon-reload
sudo systemctl start fine-report
sudo systemctl enable fine-report
sudo systemctl status fine-report
自启动 - 已验证

上面通过 systemctl 的方式管理进程,制作 FineReport 时出现问题,在浏览器中访问速度非常慢(输入URL后要等待很长时间出现页面),输入账号密码登录后没有反应,不跳转进入系统。后来修改为使用脚本启动项目,并使用 /etc/rc.local 设置自启动可以成功访问并且速度很快。下面是启动项目的脚本:

#!/bin/bash

# 启动脚本配置
APP_NAME="barrel-fine-report"
JAR_FILE_NAME="barrel-fine-report.jar"
JAR_FILE_PATH="/projs/barrel/report/server"
JAR_PATH="/projs/barrel/report/server/barrel-fine-report.jar"
CONFIG_FILE="/projs/barrel/report/server/application.yml"
LOG_FILE="/var/log/barrel-fine-report.log"
STARTUP_LOG="/var/log/barrel-fine-report-startup.log"
PID_FILE="/var/run/barrel-fine-report.pid"
JAVA_OPTS="-Xms512m -Xmx512m -Dspring.config.location=$CONFIG_FILE"

# 检查必要文件是否存在
if [ ! -f "$JAR_PATH" ]; then
    echo "$(date): 错误: 找不到 JAR 文件: $JAR_PATH" >> "$STARTUP_LOG"
    exit 1
fi

if [ ! -f "$CONFIG_FILE" ]; then
    echo "$(date): 警告: 找不到配置文件: $CONFIG_FILE" >> "$STARTUP_LOG"
fi

# 检查 Java 是否安装
if ! command -v java &> /dev/null; then
    echo "$(date): 错误: Java 未安装" >> "$STARTUP_LOG"
    exit 1
fi

# 检查是否已在运行
if [ -f "$PID_FILE" ]; then
    OLD_PID=$(cat "$PID_FILE")
    if ps -p "$OLD_PID" > /dev/null 2>&1; then
        echo "$(date): 警告: $APP_NAME 已在运行 (PID: $OLD_PID)" >> "$STARTUP_LOG"
        exit 1
    else
        echo "$(date): 清理陈旧的 PID 文件" >> "$STARTUP_LOG"
        rm -f "$PID_FILE"
    fi
fi

# 切换到项目目录
cd $JAR_FILE_PATH

# 启动应用
echo "$(date): 启动 $APP_NAME 服务..." >> "$STARTUP_LOG"
nohup java $JAVA_OPTS -jar $JAR_FILE_NAME >> "$LOG_FILE" 2>&1 &

# 记录进程 ID
PID=$!
echo $PID > "$PID_FILE"
echo "$(date): $APP_NAME 服务启动完成,PID: $PID" >> "$STARTUP_LOG"

# 验证进程是否在运行
sleep 3
if ps -p "$PID" > /dev/null 2>&1; then
    echo "$(date): 验证: 进程 $PID 正在运行" >> "$STARTUP_LOG"
else
    echo "$(date): 错误: 进程 $PID 启动后立即退出" >> "$STARTUP_LOG"
    rm -f "$PID_FILE"
    exit 1
fi

下面是关闭项目的脚本,要配套使用,启动脚本中指定了 PID 文件所在的路径,关闭脚本中要用到

#!/bin/bash

# 关闭脚本配置
APP_NAME="barrel-fine-report"
PID_FILE="/var/run/barrel-fine-report.pid"
LOG_FILE="/var/log/barrel-fine-report.log"
SHUTDOWN_LOG="/var/log/barrel-fine-report-shutdown.log"

# 记录关闭开始时间
echo "$(date): 开始停止 $APP_NAME 服务..." >> "$SHUTDOWN_LOG"

# 方法1: 通过 PID 文件停止进程
if [ -f "$PID_FILE" ]; then
    PID=$(cat "$PID_FILE")
    if ps -p "$PID" > /dev/null 2>&1; then
        echo "$(date): 通过 PID 文件找到进程 $PID,正在停止..." >> "$SHUTDOWN_LOG"
        kill "$PID"
        
        # 等待进程结束,最多30秒
        TIMEOUT=30
        while [ $TIMEOUT -gt 0 ] && ps -p "$PID" > /dev/null 2>&1; do
            sleep 1
            TIMEOUT=$((TIMEOUT-1))
        done
        
        # 如果进程还在,强制杀死
        if ps -p "$PID" > /dev/null 2>&1; then
            echo "$(date): 正常停止失败,强制杀死进程 $PID..." >> "$SHUTDOWN_LOG"
            kill -9 "$PID"
            sleep 2
        fi
        
        # 确认进程已停止
        if ps -p "$PID" > /dev/null 2>&1; then
            echo "$(date): 错误: 无法停止进程 $PID" >> "$SHUTDOWN_LOG"
            exit 1
        else
            echo "$(date): 成功停止进程 $PID" >> "$SHUTDOWN_LOG"
            rm -f "$PID_FILE"
        fi
    else
        echo "$(date): 警告: PID 文件存在但进程 $PID 未运行" >> "$SHUTDOWN_LOG"
        rm -f "$PID_FILE"
    fi
else
    echo "$(date): 警告: 未找到 PID 文件 $PID_FILE" >> "$SHUTDOWN_LOG"
fi

制作统一管理脚本 barrel-fine-report-service.sh

#!/bin/bash

# 服务管理脚本
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
START_SCRIPT="/projs/barrel/report/bin/start-barrel-fine-report.sh"
STOP_SCRIPT="/projs/barrel/report/bin/stop-barrel-fine-report.sh"
PID_FILE="/var/run/barrel-fine-report.pid"
APP_NAME="barrel-fine-report"
JAR_FILE_NAME="barrel-fine-report.jar"

case "$1" in
    start)
        echo "启动 $APP_NAME 服务..."
        $START_SCRIPT
        ;;
    stop)
        echo "停止 $APP_NAME 服务..."
        $STOP_SCRIPT
        ;;
    restart)
        echo "重启 $APP_NAME 服务..."
        $STOP_SCRIPT
        sleep 3
        $START_SCRIPT
        ;;
    status)
        if [ -f "$PID_FILE" ]; then
            PID=$(cat "$PID_FILE")
            if ps -p "$PID" > /dev/null 2>&1; then
                echo "$APP_NAME 正在运行 (PID: $PID)"
                exit 0
            else
                echo "$APP_NAME PID 文件存在但进程未运行"
                exit 1
            fi
        else
            # 检查是否有通过其他方式启动的进程
            PROCESSES=$(ps aux | grep "$JAR_FILE_NAME" | grep -v grep)
            if [ -n "$PROCESSES" ]; then
                echo "$APP_NAME 在运行但没有 PID 文件:"
                echo "$PROCESSES"
                exit 0
            else
                echo "$APP_NAME 未运行"
                exit 3
            fi
        fi
        ;;
    *)
        echo "用法: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

对于进程管理的命令如下

# 查看进程状态
sudo ./barrel-fine-report-service status
# 启动项目
sudo ./barrel-fine-report-service start
# 停止项目
sudo ./barrel-fine-report-service stop

创建两个脚本文件后记得使用 sudo chmod +x start-barrel-fine-report.sh 赋予可执行的权限,下面通过 rc.local 设置开机启动

sudo vim /etc/rc.local

# 在该文件的末尾添加下面几行代码
# 等待系统基本服务启动完成
sleep 10

# 启动 barrel 服务
/projs/barrel/report/bin/start-barrel-fine-report.sh

# 创建符号链接
sudo ln -s /projs/barrel/backend/bin/barrel-service.sh /usr/local/bin/barrel-service
# 此后在任意路径都可执行下面命令
barrel-service status
barrel-service start
barrel-service stop

# 按照名称模糊查找软连接
# 查找包含 barrel 的软连接
find / -type l -name "*barrel*" 2>/dev/null

非java管理脚本

2026年1月26日 在群晖安装部署 万吉 时使用 Grok 制作的管理脚本如下

#!/bin/bash

# 服务配置
SERVICE_NAME="lucky"
BIN_DIR="/opt/wanji"
EXECUTABLE="$BIN_DIR/lucky"
PIDFILE="/var/run/${SERVICE_NAME}.pid"
LOGFILE="/var/log/${SERVICE_NAME}.log"  # 可选日志文件,如果不需要可改为 /dev/null

start() {
    if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
        echo "$SERVICE_NAME is already running."
        return 1
    fi
    echo "Starting $SERVICE_NAME..."
    cd "$BIN_DIR" || exit 1
    nohup ./"$(basename "$EXECUTABLE")" >> "$LOGFILE" 2>&1 &
    echo $! > "$PIDFILE"
    if kill -0 $(cat "$PIDFILE") 2>/dev/null; then
        echo "$SERVICE_NAME started."
    else
        echo "Failed to start $SERVICE_NAME."
        rm -f "$PIDFILE"
        return 1
    fi
}

stop() {
    if [ ! -f "$PIDFILE" ] || ! kill -0 $(cat "$PIDFILE") 2>/dev/null; then
        echo "$SERVICE_NAME is not running."
        return 1
    fi
    echo "Stopping $SERVICE_NAME..."
    kill $(cat "$PIDFILE")
    rm -f "$PIDFILE"
    echo "$SERVICE_NAME stopped."
}

status() {
    if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
        echo "$SERVICE_NAME is running (PID: $(cat "$PIDFILE"))."
    else
        echo "$SERVICE_NAME is stopped."
    fi
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)
        status
        ;;
    *)
        echo "Usage: $0 {start|stop|status}"
        exit 1
        ;;
esac

exit 0

按照下面步骤设置文件可执行权限以及设置开机自启动

sudo cp lucky_manager.sh /usr/local/bin/luckyctl
sudo chmod +x /usr/local/bin/luckyctl
# 测试命令
luckyctl status  # 在任何路径下运行
# 启动
luckyctl start

# 设置开机自启动
sudo cp lucky_manager.sh /usr/local/etc/rc.d/S99lucky.sh
sudo chmod +x /usr/local/etc/rc.d/S99lucky.sh
# 重启NAS测试自启动

上面脚本都是测试通过的

管理脚本生成器

在为群晖制作 万吉 的管理脚本时还让 Grok 制作了生成管理脚本的脚本,制作文件 generate_service_script.sh 的内容如下

#!/bin/bash

# 默认值
SERVICE_NAME=""
BIN_DIR=""
EXECUTABLE_NAME=""
OUTPUT_FILE=""
PID_DIR="/var/run"
LOG_DIR="/var/log"

usage() {
    echo "Usage: $0 -n <service_name> -d <bin_dir> -e <executable_name> -o <output_file>"
    echo "Example: $0 -n filebrowser -d /opt/filebrowser -e filebrowser -o filebrowser_manager.sh"
    exit 1
}

while getopts ":n:d:e:o:" opt; do
    case $opt in
        n) SERVICE_NAME="$OPTARG" ;;
        d) BIN_DIR="$OPTARG" ;;
        e) EXECUTABLE_NAME="$OPTARG" ;;
        o) OUTPUT_FILE="$OPTARG" ;;
        *) usage ;;
    esac
done

if [ -z "$SERVICE_NAME" ] || [ -z "$BIN_DIR" ] || [ -z "$EXECUTABLE_NAME" ] || [ -z "$OUTPUT_FILE" ]; then
    usage
fi

EXECUTABLE="$BIN_DIR/$EXECUTABLE_NAME"
PIDFILE="$PID_DIR/${SERVICE_NAME}.pid"
LOGFILE="$LOG_DIR/${SERVICE_NAME}.log"

cat << EOF > "$OUTPUT_FILE"
#!/bin/bash

# 服务配置
SERVICE_NAME="$SERVICE_NAME"
BIN_DIR="$BIN_DIR"
EXECUTABLE="$EXECUTABLE"
PIDFILE="$PIDFILE"
LOGFILE="$LOGFILE"  # 可选日志文件,如果不需要可改为 /dev/null

start() {
    if [ -f "\$PIDFILE" ] && kill -0 \$(cat "\$PIDFILE") 2>/dev/null; then
        echo "\$SERVICE_NAME is already running."
        return 1
    fi
    echo "Starting \$SERVICE_NAME..."
    cd "\$BIN_DIR" || exit 1
    nohup ./"\$(basename "\$EXECUTABLE")" >> "\$LOGFILE" 2>&1 &
    echo \$! > "\$PIDFILE"
    if kill -0 \$(cat "\$PIDFILE") 2>/dev/null; then
        echo "\$SERVICE_NAME started."
    else
        echo "Failed to start \$SERVICE_NAME."
        rm -f "\$PIDFILE"
        return 1
    fi
}

stop() {
    if [ ! -f "\$PIDFILE" ] || ! kill -0 \$(cat "\$PIDFILE") 2>/dev/null; then
        echo "\$SERVICE_NAME is not running."
        return 1
    fi
    echo "Stopping \$SERVICE_NAME..."
    kill \$(cat "\$PIDFILE")
    rm -f "\$PIDFILE"
    echo "\$SERVICE_NAME stopped."
}

status() {
    if [ -f "\$PIDFILE" ] && kill -0 \$(cat "\$PIDFILE") 2>/dev/null; then
        echo "\$SERVICE_NAME is running (PID: \$(cat "\$PIDFILE"))."
    else
        echo "\$SERVICE_NAME is stopped."
    fi
}

case "\$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)
        status
        ;;
    *)
        echo "Usage: \$0 {start|stop|status}"
        exit 1
        ;;
esac

exit 0
EOF

echo "Generated service script: $OUTPUT_FILE"
chmod +x "$OUTPUT_FILE"

使用命令 chmod +x generate_service_script.sh 赋予可执行权限,如果要声明软件名称是 filebrowser 的管理脚本,则

# 生成管理脚本
./generate_service_script.sh -n filebrowser -d /opt/filebrowser -e filebrowser -o filebrowser_manager.sh

# 制作软连接
sudo cp filebrowser_manager.sh /usr/local/bin/filebrowserctl
sudo chmod +x /usr/local/bin/filebrowserctl
# 测试命令
luckyctl status  # 在任何路径下运行

# 设置开机自启动
sudo cp filebrowser_manager.sh /usr/local/etc/rc.d/S99filebrowser.sh
sudo chmod +x /usr/local/etc/rc.d/S99filebrowser.sh
# 重启NAS测试自启动