脚本如何恢复崩溃异常进程程序

wen 实用脚本 27

本文目录导读:

脚本如何恢复崩溃异常进程程序

  1. 核心思路:启动 -> 检测 -> 重启
  2. 方法一:使用 Shell 脚本自守护 (最简单,最常用)
  3. 方法二:使用 supervisor (跨语言进程管理工具)
  4. 方法三:使用 systemd (Linux 系统服务)
  5. 方法四:使用 Docker (容器自动重启)
  6. 进阶:带指数退避的守护脚本 (防止崩溃风暴)

针对脚本恢复崩溃或异常退出的进程,有多种策略,最常用的方法包括进程守护脚本使用系统服务 (systemd)容器编排 (如 Docker restart policy)

以下是几种常见的实现方式,从简单到复杂:

核心思路:启动 -> 检测 -> 重启

无论哪种方法,核心逻辑都是:

  1. 尝试启动目标程序。
  2. 检查程序是否正常运行(通过PID、端口、进程名、进程退出码等)。
  3. 如果检测到进程退出或崩溃,立即重新启动。
  4. (可选)加入退避策略,防止频繁重启导致资源耗尽。

使用 Shell 脚本自守护 (最简单,最常用)

这是最常见的做法,直接用一个循环包裹目标命令。

示例:restart_daemon.sh

#!/bin/bash
# 要监控的目标程序路径和参数
TARGET_COMMAND="/usr/local/bin/my_server"
# 可选:日志文件
LOG_FILE="/var/log/my_server_restart.log"
# 循环执行
while true; do
    echo "$(date) - 启动进程: ${TARGET_COMMAND}" >> "${LOG_FILE}"
    # 启动目标程序,注意:这里必须保持前台运行,不能后台运行(&)!
    # 如果目标程序可以后台运行,需要改用 stdin/stdout 重定向或等效工具。
    ${TARGET_COMMAND}
    # 当进程退出时(无论正常退出 exit 0 还是崩溃),代码会执行到此处
    EXIT_CODE=$?
    echo "$(date) - 进程退出,退出码: ${EXIT_CODE}" >> "${LOG_FILE}"
    # 可选:防止疯狂重启,等待几秒
    # 如果退出码是正常退出(0 或 130),可以决定是否继续重启
    if [ "${EXIT_CODE}" -eq 0 ]; then
        echo "$(date) - 进程正常退出,守护脚本也将停止。" >> "${LOG_FILE}"
        exit 0
    fi
    echo "$(date) - 进程异常退出,2秒后重启..." >> "${LOG_FILE}"
    sleep 2
done

如何使用:

  1. 将上述脚本保存为 restart_daemon.sh
  2. 修改 TARGET_COMMAND 变量为你的真实程序路径。
  3. 给予执行权限:chmod +x restart_daemon.sh
  4. 在前台运行(或通过 nohup/screen/tmux 运行):./restart_daemon.sh

关键点: 脚本中的 TARGET_COMMAND 必须以前台模式运行,并且其进程会被 wait 等待,这样当它崩溃时,脚本才能立即知道。


使用 supervisor (跨语言进程管理工具)

supervisor 是 Python 开发的进程管理工具,专门用于监控和重启进程,它比纯脚本更稳定,自带日志管理。

安装:

pip install supervisor  # 或 apt-get install supervisor (Ubuntu/Debian)

配置文件 (e.g., /etc/supervisor/conf.d/myapp.conf):

[program:myapp]
command=/usr/local/bin/my_server --port=8080
directory=/opt/myapp
user=www-data
autostart=true
autorestart=unexpected  ; 非正常退出时重启;也可设为 true(总是重启)
exitcodes=0             ; 只有退出码为0才认为是正常退出
stderr_logfile=/var/log/myapp.err.log
stdout_logfile=/var/log/myapp.out.log
startretries=3          ; 启动失败重试次数
startsecs=10            ; 启动后等待10秒,如果进程还活着,则视为启动成功

使用:

supervisorctl reread
supervisorctl update
supervisorctl start myapp

优点: 自带日志轮转、远程管理、状态查看,非常适合生产环境。


使用 systemd (Linux 系统服务)

如果你的系统使用 systemd,可以直接编写一个服务文件来自动重启。

创建服务文件: /etc/systemd/system/myapp.service

[Unit]
Description=My Production App
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/local/bin/my_server --config /etc/myapp/config.yaml
# 关键:设置重启策略
Restart=on-failure        ; 非正常退出时重启;也可设为 always
RestartSec=5              ; 重启前等待5秒
StartLimitIntervalSec=60  ; 60秒内
StartLimitBurst=5         ; 最多重启5次(防止频繁崩溃导致循环重启)
# 日志
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target

使用:

systemctl daemon-reload
systemctl enable myapp
systemctl start myapp
# 查看状态
systemctl status myapp
# 查看日志
journalctl -u myapp -f

优点: 系统原生支持,资源占用低,非常稳定。


使用 Docker (容器自动重启)

如果你的程序运行在 Docker 容器中,利用 Docker 的重启策略是最简单的方式。

启动容器时:

docker run -d --restart=always --name myapp myapp_image

或者在 docker-compose.yml 中:

services:
  myapp:
    image: myapp_image
    restart: unless-stopped  # 或 always, on-failure

restart 策略选项:

  • no:不自动重启(默认)。
  • always:无论容器退出状态如何,总是重启。
  • on-failure:只在容器异常退出(非 0 退出码)时重启。
  • unless-stopped:无论退出状态如何,总是重启,除非显式使用 docker stop 停止。

优点: 与容器生命周期完美结合。


进阶:带指数退避的守护脚本 (防止崩溃风暴)

如果进程一启动就立刻崩溃,上述简单脚本会疯狂重启,消耗 CPU,你需要加入退避逻辑。

示例:带退避的 bash 守护脚本

#!/bin/bash
TARGET="my_server"
MAX_RETRIES=10
BASE_SLEEP=2
retries=0
while true; do
    echo "[$(date)] 启动: ${TARGET}" | tee -a restart.log
    # 启动前台进程
    ./${TARGET}
    EXIT_CODE=$?
    echo "[$(date)] 退出,代码: ${EXIT_CODE}" | tee -a restart.log
    # 正常退出则停止守护
    if [ "${EXIT_CODE}" -eq 0 ] || [ "${EXIT_CODE}" -eq 130 ]; then
        echo "进程正常退出,终止守护。" | tee -a restart.log
        exit 0
    fi
    retries=$((retries + 1))
    if [ "${retries}" -ge "${MAX_RETRIES}" ]; then
        echo "已崩溃 ${MAX_RETRIES} 次,停止守护" | tee -a restart.log
        exit 1
    fi
    # 指数退避:2秒、4秒、8秒...最大30秒
    sleep_time=$(( BASE_SLEEP * (2 ** (retries-1)) ))
    if [ "${sleep_time}" -gt 30 ]; then
        sleep_time=30
    fi
    echo "将在 ${sleep_time} 秒后重启 (第 ${retries} 次)" | tee -a restart.log
    sleep "${sleep_time}"
done

方法 适用场景 优点 缺点
Shell 守护脚本 快速原型、简单任务、嵌入式设备 无需额外依赖,灵活性高 日志管理、稳定性、状态监控较弱
Supervisor 通用生产环境、Python/Node.js等 成熟稳定,自带日志、状态、远程控制 需要安装 Python 和 Supervisor
systemd Linux 系统原生服务 系统级集成,最稳定,资源占用最低 仅限 Linux,配置相对复杂
Docker 容器化部署 与容器生命周期完美结合,跨平台 需要容器化环境,有一定启动开销

推荐:

  • 开发/测试/微服务Docker + restart: on-failure
  • Linux 服务器生产环境systemd (最简单、最稳定)
  • 非容器化、或需要复杂日志管理Supervisor
  • 非常简单的脚本/工具Shell 守护脚本

选择哪种方式取决于你的部署环境和对稳定性的需求,对于大多数生产场景,推荐优先考虑 systemdDocker

抱歉,评论功能暂时关闭!