linux websocket高并发需调优文件描述符、nginx代理配置、fastapi广播逻辑及集群分片:①ulimit -n设为65535以上并调优内核参数;②nginx必须透传upgrade/connection头且关闭缓冲;③广播须异步解耦并限制连接数;④集群采用用户id分片+定向redis通信。

Linux文件描述符和内核参数必须改
每个WebSocket连接至少占1个fd,默认ulimit -n是1024,连到1000就可能开始静默丢连接,日志里反复出现Too many open files就是它在报警。
临时改:在启动服务的同一shell里执行ulimit -n 65535;永久改:往/etc/security/limits.conf加两行:* soft nofile 1048576和* hard nofile 1048576,并确认Nginx、Workerman或FastAPI进程由该用户启动。
同步调优内核参数(/etc/sysctl.conf):
-
net.core.somaxconn = 65535(监听队列长度) -
net.ipv4.tcp_max_syn_backlog = 262144(SYN队列) -
net.ipv4.tcp_tw_reuse = 1(复用TIME_WAIT端口) -
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。
location块里这三行缺一不可:
proxy_http_version 1.1proxy_set_header Upgrade $http_upgradeproxy_set_header Connection "upgrade"
另外两个关键配置常被忽略:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
-
proxy_read_timeout和proxy_send_timeout建议设为86400(24小时),否则空闲心跳期间连接会被Nginx主动断开 - 必须加
proxy_buffering off和proxy_cache off,否则消息可能卡在缓冲区延迟甚至丢失
FastAPI WebSocket连接池与广播必须异步解耦
直接在websocket_endpoint里用for client in active_connections逐个send_json,连接数一过千,事件循环就卡住,CPU不涨但内存持续上涨——这是典型的同步广播阻塞。
正确做法是把广播从连接生命周期里剥离开:
- 用
asyncio.Queue做消息中转,所有发信走put(),后台任务统一get()后批量分发 - 广播时改用
asyncio.gather(*tasks, return_exceptions=True),避免单个失败中断全体 - 连接数硬限建议写在
connect入口,超限时直接await websocket.close(code=1008),别等accept之后再踢人
示例节选:
async def broadcast(self, message: dict):
tasks = [self._safe_send(ws, message) for ws in self._active_connections.values()]
await asyncio.gather(*tasks, return_exceptions=True)
单机到集群的跨节点通信不能靠轮询
单机撑到5万连接已接近极限,再往上必须拆。但直接上Redis Pub/Sub不是万能解——如果所有实例都订阅同一个broadcast频道,每条消息会被每个实例重复消费+重推,10万连接×10个实例=100万次推送,带宽和CPU双爆。
更稳的路径是分片+定向:
- 按
user_id % shard_count把用户固定分配到某台WS服务,同组内广播只在本机完成 - 跨组通信走Redis,但频道名带分片标识,如
shard_3_event,只让对应实例订阅 - 用
aioredis的Subscribe配合Channel接收,收到后查本地active_connections匹配目标client_id再投递
分片数建议从5起步,单片控制在2万连接以内,广播延迟可压到10ms内。别迷信“全量同步”,真实业务里80%消息根本不需要跨片。
真正卡脖子的从来不是代码写法,而是fd没放开、Upgrade头被吃掉、广播逻辑黏在连接里、以及想用单点思维解分布式问题。这些地方动一下,连接数就能翻倍,不动的话,换再快的CPU也没用。










