本文目录导读:

- 使用循环与异常捕获(基础保活)
- 使用信号处理(优雅退出)
- 使用 Supervisor(推荐方式)
- 使用 systemd(Linux 系统推荐)
- 自愈型守护进程(PHP 内置 fallback)
- 进阶技巧:内存监控与重启
- 防止内存泄漏
- 完整的保活方案示例
在 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();
}
使用信号处理(优雅退出)
通过捕获信号(如 SIGTERM、SIGINT)来实现优雅关闭,而不是直接杀死进程。
<?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 代码逻辑,只需部署外部配置即可。