PHP守护进程如何保活

wen PHP项目 1

本文目录导读:

PHP守护进程如何保活

  1. 使用循环与异常捕获(基础保活)
  2. 使用信号处理(优雅退出)
  3. 使用 Supervisor(推荐方式)
  4. 使用 systemd(Linux 系统推荐)
  5. 自愈型守护进程(PHP 内置 fallback)
  6. 进阶技巧:内存监控与重启
  7. 防止内存泄漏
  8. 完整的保活方案示例

在 PHP 中实现守护进程的“保活”(即防止进程意外退出或崩溃后无法恢复)有多种策略,以下是几种常用的方法和最佳实践:

使用循环与异常捕获(基础保活)

这是最直接的方式,通过无限循环让进程持续运行,并处理可能出现的异常。

<?php
declare(ticks=1);
// 设置为守护进程(脱离终端)
function daemonize() {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('fork failed');
    } elseif ($pid > 0) {
        exit(0); // 父进程退出
    }
    // 子进程成为会话领导者
    if (posix_setsid() == -1) {
        die('setsid failed');
    }
    // 二次 fork 防止重新获取控制终端
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('fork failed');
    } elseif ($pid > 0) {
        exit(0);
    }
    // 切换工作目录
    chdir('/');
    // 关闭标准输入输出
    fclose(STDIN);
    fclose(STDOUT);
    fclose(STDERR);
}

在循环中捕获异常,避免进程崩溃:

while (true) {
    try {
        // 你的业务逻辑
        processTask();
    } catch (Throwable $e) {
        // 记录错误日志
        logError($e->getMessage());
        // 稍作延迟,避免死循环导致 CPU 100%
        sleep(1);
    }
    // 防止内存溢出,定期清理
    gc_collect_cycles();
}

使用信号处理(优雅退出)

通过捕获信号(如 SIGTERMSIGINT)来实现优雅关闭,而不是直接杀死进程。

<?php
// 声明信号处理器
pcntl_async_signals(true);
$running = true;
function shutdown() {
    global $running;
    $running = false;
}
pcntl_signal(SIGTERM, 'shutdown');
pcntl_signal(SIGINT, 'shutdown');
while ($running) {
    // 业务逻辑
    processTask();
    sleep(1);
}
// 清理资源
cleanup();

使用 Supervisor(推荐方式)

这是目前最主流、最可靠的方式。Supervisor 是一个进程管理工具,专门用来监控和管理进程。

配置示例 (/etc/supervisor/conf.d/my-worker.conf):

[program:my-worker]
command=php /path/to/worker.php
directory=/path/to/project
autostart=true
autorestart=true
startretries=10
startsecs=3
redirect_stderr=true
stdout_logfile=/var/log/worker.out.log
stderr_logfile=/var/log/worker.err.log
stopwaitsecs=5

优势

  • 崩溃后自动重启
  • 可以配置重启策略(如重启尝试次数)
  • 日志管理完善
  • 支持在服务器启动时自动加载

使用 systemd(Linux 系统推荐)

将 PHP 守护进程注册为 systemd 服务,利用 systemd 的进程管理能力。

创建 /etc/systemd/system/my-worker.service

[Unit]
Description=PHP Worker Daemon
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
ExecStart=/usr/bin/php /path/to/worker.php
Restart=always
RestartSec=10
KillSignal=SIGQUIT
TimeoutStopSec=10
[Install]
WantedBy=multi-user.target

启用并启动服务:

systemctl daemon-reload
systemctl enable my-worker
systemctl start my-worker

自愈型守护进程(PHP 内置 fallback)

如果环境不允许安装 Supervisor 或 systemd,可以通过一个“监视进程”来实现自我修复。

<?php
// auto_restart.php
while (true) {
    $command = "php /path/to/worker.php";
    $output = [];
    $return_var = 0;
    echo "Starting worker...\n";
    exec($command, $output, $return_var);
    echo "Worker exited with code: $return_var\n";
    // 如果是正常退出(例如收到停止信号),则退出监视器
    if ($return_var === 0) {
        break;
    }
    // 等待后重新启动
    sleep(2);
}

进阶技巧:内存监控与重启

在守护进程内部监控内存使用情况,当内存占用超过阈值时,优雅退出,让外层监控工具(如 Supervisor)重启。

while (true) {
    $memory_usage = memory_get_usage(true);
    if ($memory_usage > 500 * 1024 * 1024) { // 500MB 阈值
        logError("Memory limit reached: {$memory_usage}");
        exit(0); // 正常退出,由 Supervisor 自动重启
    }
    processTask();
    // 定期释放临时资源
    unset($tempData);
    gc_collect_cycles();
    usleep(100000); // 100ms 休眠
}

防止内存泄漏

长期运行的 PHP 进程最需要注意的是内存泄漏问题:

  • 使用 unset() 释放大型对象和数组
  • 使用 gc_collect_cycles() 强制垃圾回收
  • 避免闭包持有上下文引用
  • 定期重新初始化实例
while (true) {
    // 每次循环创建新的资源,而不是复用一个长期实例
    $db = getDatabaseConnection();
    processTask($db);
    $db = null; // 释放连接
    // 清理输出缓冲
    if (ob_get_level() > 0) {
        ob_end_clean();
    }
    sleep(1);
}

完整的保活方案示例

结合以上所有技巧的守护进程模板:

<?php
declare(ticks=1);
// 1. 守护化
daemonize();
// 2. 设置信号处理器
pcntl_async_signals(true);
$running = true;
pcntl_signal(SIGTERM, function() use (&$running) {
    $running = false;
});
// 3. 主循环
while ($running) {
    try {
        $memory = memory_get_usage(true);
        if ($memory > 500 * 1024 * 1024) {
            throw new RuntimeException("Memory limit exceeded");
        }
        processTask();
    } catch (Throwable $e) {
        file_put_contents('/var/log/worker_errors.log', 
            date('Y-m-d H:i:s') . " - " . $e->getMessage() . PHP_EOL, 
            FILE_APPEND);
        if ($e->getMessage() === "Memory limit exceeded") {
            exit(0); // 让 Supervisor 重启
        }
        sleep(1);
    }
    gc_collect_cycles();
    usleep(100000);
}
function processTask() {
    // 实际业务逻辑
    // ...
}

策略 适用场景 可靠性
Supervisor 生产环境
systemd Linux 系统服务
循环+异常捕获 简单应用
信号处理+循环 需要优雅关闭
自愈监控进程 无外部依赖

推荐使用 Supervisor 或 systemd,它们提供最稳定、最完善的进程生命周期管理,而且不需要修改你的 PHP 代码逻辑,只需部署外部配置即可。

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