弹幕系统高并发崩溃主因是服务端未做消息过滤、去重和广播分流;真实流程为接收→校验→过滤→限流→缓存→广播,须校验长度、用户合法性、时间戳,丢弃违禁/重复内容,用Redis去重,分房广播替代全量遍历。
高并发下弹幕系统崩得快,不是因为 websocket 本身扛不住,而是服务端没做消息过滤、去重和广播分流——直接把原始用户输入全量广播,10 万观众在线时,一条弹幕可能触发 10 万次 dom 插入+渲染,前端卡死,后端 cpu 爆表。
WebSocket 连接建立后,别急着广播 message
很多新手一拿到 ws.on('message', ...) 就立刻调用 broadcast(),这是最大误区。抖音、B站等平台的真实流程是:接收 → 校验 → 过滤 → 限流 → 缓存 → 广播。跳过任意一环,在千人以上房间就会出问题。
- 必须校验
content长度(建议 ≤ 20 字)、userId是否合法、timestamp是否在合理偏移范围内(如 ±5 秒) - 空内容、纯表情、连续重复字符(如
"!!!!")、含明确违禁词的,应直接丢弃,不进队列 - 服务端收到弹幕后,先写入内存队列(如
Redis List或ConcurrentLinkedQueue),再由独立消费者线程异步处理,避免阻塞连接事件循环
ws.send() 前必须做弹幕去重与幂等判断
重复弹幕不是“体验差”,而是系统设计缺陷。抖音实测中,3% 的弹幕因网络抖动或客户端重试被重复送达;不处理会导致榜单错乱、特效重复播放、数据库主键冲突。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 以
msgId+traceId拼接为去重指纹(单字段不可靠,msgId在某些场景会复用) - 服务端用
Redis SETEX存储指纹,TTL 设为 60 秒(覆盖最大网络延迟窗口) - 若
SETNX返回 0,说明已存在,直接return,不进入后续流程 - 切勿用
HashMap或进程内缓存——集群部署时各节点状态不一致,去重失效
广播阶段必须按房间/用户分组,不能无差别 ws.clients.forEach(...)
全量遍历 ws.clients 是最常见性能黑洞。10 万连接时,每次广播都要遍历 10 万个 socket 对象,即使每个只花 0.1ms,也要 10 秒——早已超时断连。
- 用
Redis Pub/Sub或Kafka替代内存广播:服务端只向频道room:123456发一次,各业务节点订阅后按需推送给本机连接 - 前端连接时必须携带
room_id参数,服务端用Map<string set>></string>维护房间映射(注意并发安全) - 对离线用户、低版本浏览器(不支持
transform动画)、弱网设备,可降级为「聚合弹幕」:每 2 秒合并最近 5 条,减少渲染压力
真正难的不是连上 WebSocket,而是让每条弹幕在毫秒级延迟下,精准、不重、不漏地落到正确用户的正确时间点上。消息指纹、分房广播、异步过滤这三步,少一个,上线即告警。

















