本文目录导读:

- 方案一:基于 WebSocket 的长连接(最推荐)
- 方案二:基于 Server-Sent Events (SSE) + AJAX
- 方案三:传统 AJAX 轮询(最不推荐)
- 核心后端业务逻辑(无论哪种方案都需要)
- 总结建议
在 PHP 项目中实现直播弹幕的后端收发处理,最核心的挑战是 HTTP 协议的无状态和单向性,传统的 HTTP 请求(用户发一条、服务器回一条)无法满足弹幕这种高并发、低延迟、频繁双向的通信需求。
纯粹的 PHP(常驻进程模式,如 Workerman、Swoole)或者配合 Node.js/Go 等更擅长处理长连接的语言是常见方案。
以下是几种成熟的后端处理方案,从简单到复杂,按推荐程度排序:
基于 WebSocket 的长连接(最推荐)
这是目前直播弹幕系统最主流、体验最好的方案,PHP 需要脱离传统的 Apache/Nginx + mod_php 模式,使用常驻内存的框架。
技术栈: Workerman 或 Swoole WebSocket 服务器
核心流程:
- 建立连接: 客户端(浏览器/APP)与 PHP WebSocket 服务器建立长连接(ws:// 或 wss://)。
- 身份验证: 建立连接后,客户端发送认证数据(用户Token、直播间ID),PHP 服务器验证并记录
fd(连接标识符)与room_id(直播间ID)的绑定关系。 - 消息接收: 客户端发送弹幕消息(JSON格式:
{"type":"danmu","content":"666"}),服务器onMessage事件接收。 - 消息处理(业务逻辑):
- 过滤敏感词。
- 检查用户权限(是否禁言、等级够不够)。
- 保存到数据库(MySQL/Redis),为了性能,通常先写入 Redis 队列,再由另一个 PHP 进程异步写入 MySQL。
- 消息广播: 将处理后的弹幕消息,发送给同一直播间内的所有其他在线客户端,Workerman/Swoole 可以轻松实现“向某个房间所有连接发送数据”。
关键代码伪代码(Workerman 示例):
// start.php (使用 Workerman 启动)
use Workerman\Worker;
use Workerman\Lib\Timer;
$ws_worker = new Worker('websocket://0.0.0.0:2346');
// 维护连接与房间关系
$ws_worker->onConnect = function($connection) {
$connection->roomId = null; // 初始无房间
};
$ws_worker->onMessage = function($connection, $data) {
$msg = json_decode($data, true);
if ($msg['type'] == 'auth') {
// 验证Token,假定有效
$connection->uid = $msg['uid'];
$connection->roomId = $msg['room_id'];
// 加入房间组 (Workerman 的 Channel 组件)
// 这里只是简单记录
echo "User {$connection->uid} joined room {$connection->roomId}\n";
}
elseif ($msg['type'] == 'danmu') {
// 1. 敏感词过滤 (假设已实现)
$cleanContent = filterDanmu($msg['content']);
// 2. 保存到 Redis 队列 (高性能写入)
$redis->lPush('danmu_queue', json_encode($msg));
// 3. 广播给房间内其他人
foreach ($ws_worker->connections as $conn) {
if ($conn->roomId == $connection->roomId && $conn->uid != $connection->uid) {
$conn->send(json_encode([
'type' => 'danmu',
'uid' => $connection->uid,
'content' => $cleanContent,
]));
}
}
}
};
$ws_worker->onClose = function($connection) {
echo "User {$connection->uid} left room {$connection->roomId}\n";
};
Worker::runAll();
优点:
- 延迟极低(毫秒级)。
- 服务器主动推送,节省带宽。
- 支持高并发(一个 PHP 进程可以处理成千上万连接)。
缺点:
- 需要修改服务器架构,不能用普通的 Apache/Nginx。
- 代码需要常驻内存,与传统 PHP 开发习惯不同,需要注意内存泄漏。
基于 Server-Sent Events (SSE) + AJAX
这是一种“半双工”方案,PHP 只负责推送(弹幕显示),客户端发弹幕仍然用 HTTP POST,优点是相对简单,但高并发下瓶颈在 HTTP 连接数。
核心流程:
- 客户端发弹幕(HTTP POST): 用户发送内容 -> 后端 PHP 接收 -> 写入 Redis 列表(如
room:123:danmu)。 - 客户端收弹幕(SSE): 页面加载时建立一个
EventSource连接到 PHP 的sse.php。 - 后端(sse.php):
- 设置
header('Content-Type: text/event-stream');,禁用缓存。 - 进入一个 死循环。
- 每次循环从 Redis 中
BRPOP(阻塞弹出)或LRANGE获取新弹幕。 - 如果有新弹幕,
echo "data: " . json_encode($danmu) . "\n\n"; ob_flush(); flush(); sleep(1);控制轮询频率。
- 设置
关键代码(sse.php):
// sse.php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
$roomId = $_GET['room_id'] ?? 1;
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$lastId = 0;
while (true) {
// 从Redis中获取最新弹幕 (使用队列方式)
$danmu = $redis->rPop("room:{$roomId}:danmu");
if ($danmu) {
echo "data: {$danmu}\n\n";
ob_flush();
flush();
}
// 检测客户端是否断开连接
if (connection_aborted()) {
break;
}
usleep(200000); // 0.2秒轮询一次
}
优点:
- 实现简单,兼容性好(标准 HTTP)。
- 后端写的是传统 PHP 脚本。
缺点:
- 单向: 只解决“推送”问题,发弹幕仍需额外接口。
- 连接数限制: Apache/Nginx 默认配置下,一个进程只能处理一个 SSE 连接,并发能力较弱,通常需要 Nginx 反向代理并开启长连接支持。
- 仍然有轮询开销。
传统 AJAX 轮询(最不推荐)
流程:
- 客户端每隔 1-3 秒发送一个 GET 请求查询新弹幕。
- 服务器端查询数据库或 Redis。
缺点:
- 延迟高: 最快也要 1 秒。
- 服务器压力大: 大量无数据请求浪费资源。
- 带宽浪费: HTTP 头部信息开销大。
核心后端业务逻辑(无论哪种方案都需要)
无论采用哪种通信方案,以下逻辑都是核心:
-
数据存储:
- 缓存层(Redis): 实时弹幕的临时存储、敏感词过滤、在线人数、用户加入的房间信息。
- 持久化层(MySQL): 直播回放时,用户查看历史弹幕,通常每个直播间每秒可能写入几十条,需要异步批量写入。
-
安全与过滤:
- 频率限制: 每个用户每 0.5 秒只能发 1 条(UserID + 直播间 + IP 三重限制,使用 Redis INCR + EXPIRE)。
- 内容过滤: SFW 过滤、广告链接过滤、重复内容判断(防止刷屏)。
- 全链条校验: 用户是否已关注主播、是否购买过道具、弹幕长度限制、直播间 ID 是否有效。
-
性能优化(关键):
- 异步写库: 弹幕写入 MySQL 必须异步,使用 Redis 列表/Stream 做消息队列,启动一个独立的 PHP CLI 进程(
php consume.php)读取队列并批量写入 MySQL,这样可以避免卡 WebSocket/SSE 主流程。 - 广播优化: 如果是万人直播间,不能逐个遍历连接发数据(会阻塞),应使用 Channel 或 Pub/Sub 机制,Workerman 有
Channel组件;Swoole 有Table+coroutine实现高性能广播。
- 异步写库: 弹幕写入 MySQL 必须异步,使用 Redis 列表/Stream 做消息队列,启动一个独立的 PHP CLI 进程(
总结建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 新项目 / 高并发 / 实时性要求高 | WebSocket (Workerman/Swoole) | 原生支持双向通信,延迟最低,性能最好,是直播弹幕的工业标准。 |
| 老项目改造 / 服务器限制 / 实时性要求一般 | SSE + AJAX | 比轮询更优,实现简单,但连接数受限。 |
| Demo演示 / 超简单场景 / 不使用常驻进程 | AJAX轮询 | 易实现,但不可用于生产环境。 |
最终结论: 如果你在做一个正式的 PHP 直播项目,强烈建议学习并使用 Workerman 或 Swoole 实现 WebSocket,花费几天时间学习这些框架,其带来的性能提升和开发体验远超传统方案,这也是 PHP 领域性能最强的方向。