千万级消息推送需分层控制:前端专注连接健康与本地限流,服务端做连接隔离、广播裁剪和消息合并,架构转向按需、分片、异步范式;浏览器端管住连接生命周期与行为收敛,服务端连接分桶、广播过滤、消息合并,传输层禁用Nagle、限制单包≤64KB并分块校验,边缘层前置过滤与实时熔断。

千万级消息推送不是靠单点优化能解决的,关键在分层控制:前端不干预分包逻辑,专注连接健康与本地限流;服务端必须做连接隔离、广播裁剪和消息合并;架构上要避免“全量推”,转向按需、分片、异步的推送范式。
浏览器端:别碰分片,但要管住自己
HTML5 WebSocket API 不暴露帧级控制能力,手动分片不可行,也不应尝试。重点落在连接生命周期管理和客户端行为收敛:
- 建立连接后立即发送轻量级心跳(如
{"type":"ping","ts":123}),并监听服务端pong响应,超时未回即主动重连 - 对高频业务消息(如实时行情、弹幕)启用客户端本地节流:用时间窗口(如100ms)聚合多条待发消息,合并为单次
send() - 监听
onclose事件时检查event.code,若为1008(策略违规)或1011(服务器过载),延迟3~10秒再重试,避免雪崩重连 - 避免在
onmessage中直接解析超大JSON,优先用ReadableStream分块读取或校验Content-Length头(若服务端封装了元数据)
服务端:连接要分桶,广播要裁剪
面对百万级连接,单集合遍历+串行写入是性能杀手。必须从数据结构和调度逻辑上重构:
- 将在线用户按
room_id % N(N建议为CPU核心数×2)打散到N个独立连接池,每个池配专属锁或读写锁,实现并发广播 - 广播前严格过滤:跳过
last_heartbeat < now - 45s的僵尸连接;按用户角色屏蔽非目标消息(如游客不收管理员指令);同一房间内对已读消息ID去重 - 对高频小消息(如位置更新、状态心跳)启用“合并推送”:同一毫秒窗口内,将发往同一连接的多条消息序列化为一个数组
["msg1","msg2","msg3"]再发送,降低帧数量级 - 每连接绑定滑动窗口计数器(如Redis Sorted Set或内存LRU),限制
5 msg/s,超限则静默丢弃并记录日志,不返回错误帧以防被滥用为探测手段
传输层:绕过分片,改用分块+校验
WebSocket协议虽支持分片,但浏览器行为不一致、服务端处理易出错。更稳的路径是应用层主动规避大包:
立即学习“前端免费学习笔记(深入)”;
- 单条消息体严格限制≤64KB(避开多数浏览器自动分片阈值),超长内容(如日志快照、配置文件)拆分为带序号的
{"id":"log-abc","seq":0,"total":3,"data":"..."}分块,由客户端拼接 - 所有分块消息携带
sha256摘要(放在首块的meta字段),客户端收齐后校验,失败则触发{"cmd":"retry","id":"log-abc"}请求重发 - 对超时未收全的分块(如5秒无新块到达),客户端主动关闭当前会话并重建连接,防止半截数据阻塞后续流
- 禁用Nagle算法(
TCP_NODELAY=true)和服务端write()调用前预分配缓冲区,减少系统调用与内存拷贝开销
边缘与监控:前置过滤 + 实时熔断
防御不能只靠业务代码,必须下沉到网络入口和可观测链路:
- 在Nginx或Cloudflare层配置
limit_conn,按真实IP(非XFF)限制单IP最大WebSocket连接数(如5),并拒绝Sec-WebSocket-Protocol非法值的握手请求 - 采集并告警核心指标:每秒新建连接突增>300%、平均连接时长<10秒、异常关闭率>5%,触发自动扩容或切换备用集群
- 对高危操作(如全服广播)强制走审批流程,后台生成带签名的推送任务单,服务端校验通过后才加入广播队列
- 所有推送消息附加
trace_id,与日志、链路追踪打通,便于故障时快速定位是哪一环丢包或卡顿



















