ThinkPHP通过Redis List实现轻量弹幕系统:LPUSH入队、LRANGE读取、LTRIM限长,按房间隔离键名并设过期时间;控制器提供/pull和/send接口,前端结合播放器事件智能轮询,MySQL异步备份保障数据安全。

ThinkPHP 本身不直接处理弹幕实时显示,它只负责接收、校验、存取和分发弹幕数据。真正的“飞入”“碰撞”“随时间精准出现”由前端控制;而高并发下的即时触达,靠 Redis List 队列 + 合理轮询策略就能在中小项目中稳住体验,无需强上 WebSocket。
Redis List 做弹幕主缓存
用 LPUSH 入队、LRANGE 读取、LTRIM 限长,是 ThinkPHP 接入弹幕最轻量可靠的方案:
- 每条弹幕存为 JSON 字符串,包含
content、time(视频秒数)、color、type(scroll/top/bottom)、user_id - 键名按房间隔离,例如
danmu:video_123,避免不同视频间干扰 - 写入后立即
LTRIM danmu:video_123 0 499,只保留最新 500 条,内存可控 - 设置过期时间
EXPIRE danmu:video_123 3600,防止冷数据堆积
ThinkPHP 控制器实现两个核心接口
在 app/controller/Danmu.php 中定义:
-
/danmu/pull:接收
video_id和可选的last_time(上次拉取的最大 time),返回该时间点之后的弹幕列表(建议最多 20 条)
内部用LRANGE danmu:video_X 0 19读取,再按time > last_time过滤并去重 -
/danmu/send:接收 POST 数据,做基础校验:
— 内容长度 ≤ 30 字(mb_strlen)
—time是合法浮点数且 ≥ 0
— 用户 5 秒内未超频(用Redis::incr("rate:uid_{$uid}")+expire实现)
校验通过后LPUSH danmu:video_X $json,不等 MySQL 写完就返回成功
前端轮询策略要“聪明”
不用每秒全量拉,而是结合播放器 timeupdate 事件动态请求:
立即学习“PHP免费学习笔记(深入)”;
- 监听视频当前播放时间
t,每次触发时请求/danmu/pull?video_id=123&last_time=t-0.5 - 响应里带
max_time字段,前端记录为下次last_time,避免重复或漏掉 - 空响应不重试;连续 3 次无新弹幕,可降频至 2 秒一次;有新弹幕则保持高频(如 500ms)
- 所有弹幕 DOM 提前创建好(池化),渲染时仅复用、设内容、启动 CSS 动画,不频繁
appendChild
MySQL 异步落库保安全
Redis 是快车道,MySQL 是保险箱。ThinkPHP 可用任务队列异步备份:
- 在 send 接口里不直接写 MySQL,而是
queue->push(new SaveDanmuJob($data)) - Job 中用
Db::table('danmu')->insert()写入,字段含video_id、content、time_offset、created_at等 - 表加联合索引
(video_id, time_offset),后续查历史回放或运营分析才走这里,不影响实时流



















