能否用CronJob替代While循环实现PHP Server-Sent-Events以解决进程堆积?
解决SSE导致Apache max_children耗尽的替代方案
问题根源
你当前用PHP无限循环实现SSE的方式,会让每个用户的SSE长连接占用一个PHP-FPM进程,进程长期处于挂起(sleep等待)状态。当并发用户量上升时,很快就会耗尽Apache或PHP-FPM的max_children阈值,导致新请求无法处理。
可行解决方案
1. 事件驱动SSE服务(推荐)
改用基于事件驱动的PHP框架(如Workerman、Swoole)实现SSE,这类框架可以通过少量进程管理数千个并发长连接,彻底解决进程堆积问题。
以Workerman为例的实现代码:
use Workerman\Worker; require_once __DIR__ . '/Workerman/Autoloader.php'; // 创建HTTP Worker监听指定端口 $worker = new Worker('http://0.0.0.0:8080'); $worker->count = 4; // 根据服务器配置调整进程数,4个进程即可应对大量并发 $worker->onMessage = function($connection, $data) { // 发送SSE响应头 $connection->send(implode("\r\n", [ 'HTTP/1.1 200 OK', 'Content-Type: text/event-stream', 'Cache-Control: no-cache', 'Connection: keep-alive', '', '' ])); // 定时检查并推送新事件,替代原有的sleep循环 $timerId = \Workerman\Timer::add(3, function() use ($connection) { // 从缓存/数据库获取当前连接用户的新事件 $newEvent = getLatestUserEvent($connection->getUserId()); if ($newEvent) { $connection->send("data: {$newEvent}\n\n"); } }); // 连接关闭时清理定时器 $connection->onClose = function() use ($timerId) { \Workerman\Timer::del($timerId); }; }; Worker::runAll();
这种模式下,单个进程可维持数百甚至上千个长连接,不会像传统PHP-FPM那样每个连接占用一个进程,从根源上避免max_children耗尽。
2. CronJob预生成消息+短连接轮询(折中方案)
如果不想引入事件驱动框架,可通过CronJob定时生成推送消息,结合客户端短连接轮询获取,替代长连接SSE:
步骤1:CronJob定时生成消息
编写PHP脚本generate_sse_events.php,负责查询新事件并写入Redis/Memcached等缓存:
$redis = new Redis(); $redis->connect('127.0.0.1', 6379); // 查询需要推送的事件(示例:从数据库获取) $events = queryNewEvents(); foreach ($events as $userId => $eventContent) { $redis->set("sse:event:{$userId}", $eventContent); // 设置过期时间,避免缓存堆积 $redis->expire("sse:event:{$userId}", 60); }
通过CronJob定时执行该脚本(比如每3秒一次):
*/3 * * * * php /path/to/generate_sse_events.php
步骤2:客户端轮询接口
客户端不再保持长连接,每隔3秒请求一次接口获取最新消息:
// 轮询接口代码 $userId = $_GET['user_id']; $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $newEvent = $redis->get("sse:event:{$userId}"); if ($newEvent) { echo "data: {$newEvent}\n\n"; $redis->del("sse:event:{$userId}"); // 避免重复推送 } else { echo "data: ping\n\n"; }
这种方式每个请求都是短连接,PHP-FPM进程处理完成后立即释放,不会长期占用资源,有效避免进程堆积。
3. 临时配置优化(仅缓解)
如果暂时无法调整架构,可通过修改配置临时缓解:
- 调整PHP-FPM的
pm.max_children:根据服务器CPU核心数和内存配置合理提高(比如CPU核心数×4) - 启用Apache的
mpm_event模式:替代默认的prefork模式,更适合处理长连接 - 设置
pm.process_idle_timeout:缩短闲置进程的回收时间,释放资源
但这只是临时方案,当用户量持续增长时,仍会遇到进程耗尽的问题。
内容的提问来源于stack exchange,提问作者Avatar
相关产品推荐
相关产品推荐

