本文目录导读:

- 核心思路:启动 -> 检测 -> 重启
- 方法一:使用 Shell 脚本自守护 (最简单,最常用)
- 方法二:使用
supervisor(跨语言进程管理工具) - 方法三:使用 systemd (Linux 系统服务)
- 方法四:使用 Docker (容器自动重启)
- 进阶:带指数退避的守护脚本 (防止崩溃风暴)
针对脚本恢复崩溃或异常退出的进程,有多种策略,最常用的方法包括进程守护脚本、使用系统服务 (systemd) 或容器编排 (如 Docker restart policy)。
以下是几种常见的实现方式,从简单到复杂:
核心思路:启动 -> 检测 -> 重启
无论哪种方法,核心逻辑都是:
- 尝试启动目标程序。
- 检查程序是否正常运行(通过PID、端口、进程名、进程退出码等)。
- 如果检测到进程退出或崩溃,立即重新启动。
- (可选)加入退避策略,防止频繁重启导致资源耗尽。
使用 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
如何使用:
- 将上述脚本保存为
restart_daemon.sh。 - 修改
TARGET_COMMAND变量为你的真实程序路径。 - 给予执行权限:
chmod +x restart_daemon.sh。 - 在前台运行(或通过 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 守护脚本
选择哪种方式取决于你的部署环境和对稳定性的需求,对于大多数生产场景,推荐优先考虑 systemd 或 Docker。