脚本如何守护进程运行

wen 实用脚本 26

从原理到实战的完整指南

文章导读目录

  1. 守护进程的核心概念与必要性
  2. 脚本守护进程的常见技术方案
  3. 手把手实现一个可靠的守护脚本
  4. 高级技巧:应对进程崩溃与资源泄漏
  5. 常见问题问答(FAQ)

守护进程的核心概念与必要性

什么是守护进程?
在Linux/Unix系统中,守护进程(daemon)是一种在后台运行、不受终端控制、生命周期贯穿整个系统运行周期的特殊进程,例如Web服务器Nginx、容器引擎Docker的运行守护进程。

脚本如何守护进程运行

为什么需要脚本守护?
开发者在测试环境或生产部署中,常遇到以下痛点:

  • 脚本因意外崩溃(如内存溢出、网络中断)后无法自动重启
  • 进程未正确处理SIGHUP信号导致终端关闭后中断
  • 缺乏监控机制,故障发现滞后

通过编写守护脚本,可解决:

  1. 自动重启:进程异常退出时0.5秒内拉起新进程
  2. 电平控制:精确管控进程启动/停止状态
  3. 资源限制:监控CPU/内存使用超阈值时主动干预

脚本守护进程的常见技术方案

方案选择需根据环境复杂度与性能要求:

简单循环检测(Bash基础方案)

#!/bin/bash
while true; do
    /path/to/your-script.sh
    sleep 2
done

适用场景:快速验证、临时任务
缺陷:无法处理僵尸进程、无日志记录

Systemd服务单元(linux原生方案)

[Unit]
Description=My Daemon Script
After=network.target
[Service]
ExecStart=/usr/local/bin/my-script.sh
Restart=always
RestartSec=3
User=myuser
[Install]
WantedBy=multi-user.target

优势:自动依赖管理、标准化状态跟踪
配置要点Restart=always确保任何退出均重启,RestartSec防止频繁重启

独立监控脚本(生产级方案)

通过子进程管理实现:

  • 主进程仅执行监控逻辑
  • 子进程执行实际业务
  • 信号转发与资源回收

手把手实现一个可靠的守护脚本

以下是一个符合生产标准的Python守护脚本(兼容Python3.6+):

#!/usr/bin/env python3
import os
import sys
import time
import signal
import subprocess
class DaemonManager:
    def __init__(self, command, max_restarts=10, restart_interval=5):
        self.command = command
        self.max_restarts = max_restarts
        self.restart_interval = restart_interval
        self.child_pid = None
        self.restart_count = 0
        self.running = True
    def start_child(self):
        """以子进程形式启动目标脚本"""
        process = subprocess.Popen(self.command, shell=True)
        self.child_pid = process.pid
        print(f"[INFO] 启动子进程 PID: {self.child_pid}")
    def monitor(self):
        """核心监控循环"""
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        self.start_child()
        while self.running:
            # 检查子进程存活状态
            try:
                pid, status = os.waitpid(self.child_pid, os.WNOHANG)
                if pid != 0:  # 子进程已退出
                    self.restart_count += 1
                    if self.restart_count > self.max_restarts:
                        print("[ERROR] 达到最大重启次数,停止守护")
                        break
                    print(f"[WARN] 进程退出,第{self.restart_count}次重启")
                    time.sleep(self.restart_interval)
                    self.start_child()
            except OSError as e:
                print(f"[ERROR] 监控异常:{e}")
            time.sleep(0.5)
    def signal_handler(self, signum, frame):
        """优雅处理终止信号"""
        print(f"[INFO] 收到信号 {signum},停止守护...")
        self.running = False
        if self.child_pid:
            os.kill(self.child_pid, signal.SIGTERM)
if __name__ == "__main__":
    # 使用示例:守护 your-script.py
    manager = DaemonManager(
        command="python3 your-script.py",
        max_restarts=5,
        restart_interval=3
    )
    manager.monitor()

关键设计点

  • 使用subprocess.Popen替代os.system避免shell注入
  • os.waitpid非阻塞检查避免僵尸进程
  • 最大重启次数防止无限循环崩溃
  • 信号转发确保子进程能正常退出

高级技巧:应对进程崩溃与资源泄漏

1 内存泄漏自动保护

import psutil
def check_resource():
    process = psutil.Process(child_pid)
    if process.memory_percent() > 80.0:  # 内存超80%
        process.kill()
        print("[ALARM] 内存泄漏风险,强制终止进程")

注意:需安装psutil库(pip install psutil

2 日志回滚机制

import logging
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler('daemon.log', maxBytes=10*1024*1024, backupCount=3)
logging.basicConfig(handlers=[handler], level=logging.INFO)

3 健康检查接口(RESTful方式)

定期用cURL检查脚本的HTTP接口:

*/5 * * * * curl -f http://127.0.0.1:8080/health || systemctl restart myscript

常见问题问答(FAQ)

Q1:为什么我的守护脚本在终端关闭后就会停止?

A:这是因为脚本没有和终端进程组脱离,解决方案:

  • 使用nohup启动(nohup python daemon.py &
  • 或在脚本开头调用os.setsid()创建新会话

Q2:如何实现每天定时重启守护进程?

A:使用crontab结合计划任务:

# 每天凌晨3点重启脚本
0 3 * * * pkill -f 'your-script'; sleep 5; /usr/local/bin/start-daemon.sh

更规范的做法是systemd的OnCalendar定时单元。

Q3:守护进程日志占用磁盘过大怎么办?

A:必须配置日志轮转,推荐方案:

  1. 使用logrotate管理系统日志
  2. 代码中实现RotatingFileHandler(参考4.2节)
  3. 限制日志保留天数(maxDays=7

Q4:多进程的脚本如何统一守护?

A:使用进程组管理,例如通过pgrep -P $PPID获取所有子进程,统一发送信号。
简化方案:改用supervisor作为监控容器(支持多个子进程配置)。

Q5:守护脚本自身崩溃怎么办?

A:有两个可靠方案:

  1. 使用systemd的Restart=always管理守护脚本本身
  2. 编写二级守护:两个脚本互相监控对方存活状态(高可用集群思想)

守护进程的核心逻辑在于:监控→检测异常→自动恢复,根据环境选择适当的轮询间隔(生产环境建议1-3秒),结合资源限制与日志管理,即可实现高可用的脚本运行环境,推荐直接使用systemd(linux)或Windows Service Manager编写正式服务,但在需要灵活控制的自建场景中,掌握文中的Python守护脚本框架足以应对90%的需求。


本文由搜索引擎资料综合实践而成,参考了Linux Systemd文档及Stackoverflow社区的最佳实践讨论。

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