websocket并发性能瓶颈主要在内存管理:禁用compression可省50kb/连接;java需合理设maxtextmessagebuffersize防oom;rust中dashmap应配weak引用;php swoole勿滥用连接池;所有语言close后须立即清理监听器和任务。

直接说结论:WebSocket并发性能瓶颈几乎都卡在内存管理上,不是CPU也不是带宽——连接数上万后,OOM和泄漏比超时更早出现。
禁用 compression 能省下 50KB/连接
Python websockets 库默认开启 permessage-deflate 压缩,每个连接额外吃掉约 50KB 内存(从 64KB → 14KB)。这不是“要不要压缩”的权衡,而是“压不压缩都得先扛住连接数”。
-
compression=None必须显式传给websockets.serve(),不写就默认启用 - 如果业务消息本身已压缩(如 JSON 中含 base64 图片),再开 WebSocket 压缩反而增加 CPU 开销
- 禁用后需确认客户端兼容性:主流浏览器(Chrome/Firefox/Safari)均支持无压缩连接,无需改前端代码
Java 的 maxTextMessageBufferSize 别设成 20MB
Tomcat/Jetty 默认 maxTextMessageBufferSize 是 8KB,但有人为了“防截断”直接设成 10 * 1024 * 1024(即 10MB),结果每个连接多占 20MB 堆内存(CharBuffer 内部是 char[],每个 char 占 2 字节)。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 查
WsFrameServer或PojoMessageHandlerWholeText的@OnMessage(maxMessageSize = ...)注解,优先用注解控制单条消息上限 - 全局配置只设为业务最大单消息体积的 1.2 倍,比如聊天消息顶多 128KB,那就设
131072 - 别信“反正内存够”,1000 个连接 × 20MB = 20GB 堆,JVM 直接 OOM 报
java.lang.OutOfMemoryError: Java heap space
Rust 中 DashMap<uuid clientsession></uuid> 必须配弱引用
用 tokio-tungstenite + dashmap 做网关时,ClientSession 里若存了 Sender<msg></msg>,又在广播逻辑里不断 clone(),Arc 引用计数永远 >1,drop 不掉。
- 心跳检测任务里不能只靠
is_connected(),要主动调client_map.remove(&id)清注册表 - 把
Sender改成Weak<sender>></sender>,广播前用upgrade()检活,失败就跳过 - 别依赖 GC 或 drop 实现清理——Rust 没 GC,Arc 生命周期全靠你手动掐断
PHP Swoole 的连接池不是万能解药
看到“连接池”就照抄 WebSocketPool 类?错。Swoole 本身是常驻进程+事件循环,connection 对象生命周期由 Worker 进程托管,自己再套一层池子反而增加锁竞争和内存碎片。
- 真正该池化的不是 WebSocket 连接,而是下游资源:DB 连接、Redis client、HTTP client
- 用
$server->connections遍历活跃连接足够快,别用ArrayList或HashMap二次缓存 fd - 心跳包用
$server->tick()统一发,别给每个连接起独立 timer,否则 10 万连接 = 10 万个定时器
最易被忽略的一点:所有语言里,close() 调用后必须立刻移除事件监听器或取消任务句柄。哪怕只漏掉一个 setInterval() 或一个未 await 的 asyncio.create_task(),连接对象就锁死在内存里——它不报错,只是静静膨胀,直到某天凌晨三点服务挂掉。










