You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

高流量PHP 5.6网站Server-Sent Event高性能实现方案咨询

针对高流量PHP网站的高性能Server-Sent Event(SSE)实现方案

首先得明确你面临的核心问题:传统PHP-FPM架构下,每个SSE长连接会占用一个FPM进程,而你日均25万UV、并发2000的场景下,这种模式会直接耗尽FPM进程池,导致新请求无法处理——这也是为什么你觉得那些长循环示例和libSSE-php不适用的原因。

下面是几个经过验证的高性能方案,适配你的PHP 5.6环境:

一、Nginx + 消息队列(Redis):最优高并发方案

这个方案的核心是把长连接的维持工作从PHP转移到Nginx,PHP只负责生产消息,不处理客户端连接,彻底解决FPM进程被占满的问题。

实现原理

  1. 客户端直接和Nginx建立SSE长连接,Nginx作为代理,监听Redis的指定频道;
  2. 当你的PHP业务系统产生通知时,通过Redis的PUBLISH命令将消息发送到对应频道;
  3. Nginx收到Redis推送的消息后,立即转发给所有订阅该频道的客户端。

关键步骤

  1. 配置Nginx:需要安装支持SSE和Redis订阅的模块(比如ngx_http_push_stream_module或OpenResty的lua-resty-redis)。示例配置大致如下:
location /sse/notifications {
    push_stream_subscriber;
    push_stream_channels_path $arg_channel;
    push_stream_message_template "data: {message}\n\n";
    push_stream_redis_host 127.0.0.1;
    push_stream_redis_port 6379;
    push_stream_redis_db 0;
    push_stream_redis_channel_prefix "sse:";
}
  1. 客户端代码:连接Nginx的SSE端点,指定用户专属频道(比如用户ID):
if (!!window.EventSource) {
    const userId = '12345'; // 替换为当前用户ID
    const source = new EventSource(`/sse/notifications?channel=${userId}`);
    source.addEventListener('message', function(e) {
        const data = JSON.parse(e.data);
        console.log('收到通知:', data);
        // 更新页面通知数量
    }, false);
    
    // 处理重连和错误
    source.addEventListener('error', function(e) {
        if (e.readyState === EventSource.CLOSED) {
            console.log('连接断开,尝试重连...');
            // 这里可以实现指数退避重连逻辑
        }
    });
}
  1. PHP业务代码:当有新通知时,推送到Redis:
<?php
// 连接Redis(需要安装phpredis扩展)
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

// 推送消息到用户专属频道
$userId = '12345';
$message = json_encode(['unread_count' => 5]);
$redis->publish("sse:{$userId}", $message);

优势

  • PHP完全脱离长连接,只做消息生产,性能开销极低;
  • Nginx天生擅长处理高并发长连接,能轻松支撑数千甚至上万的SSE连接;
  • 扩展性强,后续可以轻松扩容Redis或Nginx节点。

二、常驻进程框架(Workerman/Swoole):纯PHP生态的高性能方案

如果不想改动Nginx配置,或者更倾向于纯PHP技术栈,可以用常驻进程框架来实现SSE,这类框架基于EventLoop,一个进程能处理上千个连接,彻底摆脱FPM短进程的限制。

以Workerman为例(适配PHP 5.6)

Workerman对PHP 5.6支持良好,无需额外编译复杂模块。

1. 安装Workerman

composer require workerman/workerman:^3.5 # 3.x版本支持PHP 5.3+

2. 编写SSE服务端代码

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Workerman\Worker;

// 创建一个HTTP服务,监听8080端口
$worker = new Worker('http://0.0.0.0:8080');

// 设置进程数,根据CPU核心数调整
$worker->count = 4;

// 当客户端连接时触发
$worker->onConnect = function($connection) {
    // 设置SSE响应头
    $connection->send("HTTP/1.1 200 OK\r\n");
    $connection->send("Content-Type: text/event-stream\r\n");
    $connection->send("Cache-Control: no-cache\r\n");
    $connection->send("Connection: keep-alive\r\n");
    $connection->send("\r\n");
    
    // 记录用户ID(假设从请求参数获取)
    $userId = $_GET['user_id'] ?? '';
    if ($userId) {
        // 将连接绑定到用户频道(可以用全局数组存储,或者Redis)
        global $userConnections;
        $userConnections[$userId][] = $connection;
    }
};

// 当客户端断开连接时触发
$worker->onClose = function($connection) {
    // 从用户频道中移除连接
    global $userConnections;
    foreach ($userConnections as $userId => $connections) {
        $key = array_search($connection, $connections);
        if ($key !== false) {
            unset($userConnections[$userId][$key]);
            break;
        }
    }
};

// 启动Worker
Worker::runAll();

3. PHP业务代码推送消息

<?php
// 连接Workerman的内部通讯端口(或者用Redis中转)
$client = stream_socket_client('tcp://127.0.0.1:8080');
if (!$client) {
    die('连接失败');
}

// 构造推送消息(这里需要自定义协议,或者直接用Workerman的API)
// 示例:向指定用户发送通知数量
$userId = '12345';
$message = json_encode(['unread_count' => 5]);
$sendData = "data: {$message}\n\n";

// 找到该用户的所有连接并发送
global $userConnections;
if (isset($userConnections[$userId])) {
    foreach ($userConnections[$userId] as $connection) {
        $connection->send($sendData);
    }
}

fclose($client);

优势

  • 纯PHP实现,无需依赖Nginx模块,部署简单;
  • 常驻进程复用资源,单进程支持上千连接,性能远高于FPM;
  • 灵活度高,可以自定义消息路由和连接管理逻辑。

三、传统方案的临时优化(不推荐用于高并发)

如果暂时无法重构架构,可以尝试优化FPM配置临时过渡,但这只是权宜之计:

  • 调高pm.max_children和pm.start_servers,但会大幅增加内存消耗;
  • 启用pm.process_idle_timeout,回收闲置进程;
  • 给SSE连接设置合理的超时时间,定期断开重连。

但这种方式无法从根本上解决长连接占用进程的问题,并发量上来后依然会崩溃。

额外注意事项

  1. 心跳机制:长连接可能被网关或CDN断开,需要定期发送心跳消息(比如每隔30秒发送data: ping\n\n),客户端收到后无需处理,仅维持连接;
  2. 连接重连:客户端要处理error事件,实现自动重连(推荐指数退避策略,避免频繁重试);
  3. PHP 5.6兼容性:选择框架时要确认版本支持,比如Swoole 1.x、Workerman 3.x都兼容PHP 5.6;
  4. 消息可靠性:如果需要确保消息不丢失,可以用Redis的持久化或者消息队列的ACK机制。

内容的提问来源于stack exchange,提问作者Yuseferi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:57:38