单机WebSocket并发卡在1k–5k,主因是系统文件描述符未调优(默认1024)、Nginx未透传Upgrade头、服务端事件模型低效;三者任一未解决,65535理论上限即无法达成。

单机 WebSocket 并发量卡在 1k–5k,基本可以断定不是代码写得差,而是系统资源没放开、Nginx 没配对、服务端事件模型没选准——这三个环节任一没调好,65535 这个理论上限就永远只是理论。
Linux 文件描述符与内核参数必须改
每个 WebSocket 连接至少占用 1 个文件描述符(fd),而 Linux 默认 ulimit -n 是 1024。不改这个,listen() 队列满、accept() 失败、新连接直接被拒绝,日志里常看到 Too many open files 或静默丢连接。
- 临时生效:
ulimit -n 65535(需在启动服务的同一 shell 中执行) - 永久生效:向
/etc/security/limits.conf追加两行:* soft nofile 1048576和* hard nofile 1048576,并确认 Nginx / Workerman / GoAccess 等进程由该用户启动 - 同步调优内核:
/etc/sysctl.conf加入net.core.somaxconn = 65535、net.ipv4.tcp_tw_reuse = 1、net.ipv4.ip_local_port_range = 1024 65535,再执行sysctl -p
Nginx 代理 WebSocket 必须显式透传 Upgrade 头
90% 的“连不上”问题出在这里:Nginx 默认把 WebSocket 握手当成普通 HTTP 请求处理,过滤掉 Upgrade 和 Connection 头,导致后端收不到升级请求,返回 200 而非 101 —— 客户端报错 Unexpected response code: 200。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
-
location块中必须写全这三行:proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade" -
proxy_read_timeout和proxy_send_timeout建议设为86400(24 小时),不能沿用默认的 60 秒,否则空闲心跳期间连接会被主动切断 - 务必关闭缓冲:
proxy_buffering off;禁用缓存:proxy_cache off;否则消息可能延迟甚至丢失
服务端事件循环模型决定单机吞吐天花板
Workerman 默认用 Select,GoAccess 用 poll,Node.js 默认 libuv,Python websockets 库默认启用压缩——这些选择直接影响每连接内存开销和并发极限。
- Workerman:在
start_gateway.php中启用Libevent扩展(比Select高效得多),并把$gateway->count设为 CPU 核心数的 1–2 倍(如 8 核设 8–16) - Python
websockets:禁用压缩可将单连接内存从 ~64KB 降至 ~14KB,加参数compression=None - Java Netty:避免 BIO/NIO 混合线程模型,启用
Epoll边缘触发 +IdleStateHandler控制读超时,workerGroup线程数建议设为 CPU 核心数 × 2
连接分片与跨节点广播必须协同设计
当单机撑不住时,简单堆机器不行——没做连接分片+消息路由一致性,广播消息会指数级放大,节点间带宽打满,延迟飙升。
- 按用户 ID 哈希分片:例如 10 万连接分 10 片,每片只处理 1 万连接内的通信,广播延迟从 ~60ms 降到 ~6ms
- 负载均衡必须启用 sticky session:
ip_hash或基于 token 的一致性哈希,确保同一客户端始终落到同一后端实例 - 跨节点消息靠 Redis Pub/Sub 或 Kafka 中转,但注意:订阅者需按分片标识过滤频道,避免全量消费;发布者需根据目标用户 ID 计算所属分片再投递
最容易被忽略的是:心跳间隔、读超时、代理超时、服务端 idle timeout 四个值必须严格对齐,差 1 秒都可能导致连接被静默断开。比如客户端每 30 秒 ping,服务端 read_timeout 设了 45 秒,Nginx 却只等 30 秒就 kill 连接——这种错配在压测后期才暴露,但排查成本极高。


















