用ab压测工具跑WebSocket,力测结果在3000并发连接时服务器直接崩了。力测这不是力测段子,上个月帮朋友排查一个在线协作白板项目,力测他们用Node.js的力测ws库,单机撑到2800个连接就开始疯狂掉线,力测CPU飙到95%以上。力测问题不在带宽,力测不在内存,力测而是力测文件描述符耗尽加上事件循环被阻塞。
WebSocket压力测试跟HTTP完全两码事。力测HTTP短连接压测,力测你关心QPS、力测响应时间、力测错误率。力测WebSocket是长连接,一旦握手成功,那条TCP通道就一直占着。测试的核心指标变成:能同时维持多少活跃连接、消息广播的延迟分布、断线重连的恢复速度。很多人拿JMeter随便配个WebSocket采样器就开跑,结果测出来的数据根本没法看——因为JMeter默认每个线程独立连接,但真实场景里客户端会批量订阅、心跳保活、断线自动重连,这些行为压测工具不模拟,数据就是假的。
自己写压测客户端比现成工具靠谱
我习惯用Go写压测端。goroutine开一万个也不费劲,每个goroutine模拟一个真实用户:先连上,发订阅请求,然后按随机间隔发心跳,偶尔发一条业务消息。关键是要加连接保持时间和消息发送速率两个变量。见过一个团队用Python的asyncio写,单机只能跑800连接,换Go之后直接拉到15000,不是Python不行,是GIL和事件循环调度在长连接场景下太吃亏。
服务端这边,Linux内核参数得先调。
ulimit -n改成65535只是起步,net.core.somaxconn和net.ipv4.tcp_max_syn_backlog也得跟着改。有个坑:很多人只改/etc/security/limits.conf,忘了systemd服务自己的LimitNOFILE,结果重启服务后文件描述符又回到1024。- 还有
net.ipv4.tcp_tw_reuse,对于频繁短连接测试有用,但WebSocket长连接场景下基本用不上。
消息广播才是真正的性能杀手
维持连接本身不难,难的是广播。一个5000人的房间,有人发了一条消息,服务端要遍历所有连接逐个write。如果每个write耗时0.1ms,5000个就是500ms,这还没算网络抖动。实测数据:用Redis Pub/Sub做中转,单节点广播5000条消息平均延迟120ms,P99跑到400ms。换成Kafka分区消费,延迟降到35ms,但架构复杂度翻倍。
有个取巧的办法:把消息序列化一次,然后批量write。Node.js里用
socket.write(buffer)比逐个send()快三倍。但要注意背压,socket.bufferSize超过阈值就得暂停读取,否则内存直接爆。我见过一个项目因为没处理背压,压测到2000连接时Node进程吃了8GB内存,最后OOM。断线重连的测试经常被忽略
真实网络里客户端会掉线、会切换WiFi、会锁屏。压测时应该随机kill掉10%的连接,看服务端多久能清理死连接,客户端多久能重连成功。用
ss -s看TIME_WAIT数量,如果清理不及时,新连接根本建不起来。有个社交App的案例:压测时故意断开30%连接,结果重连风暴直接把负载均衡打挂,因为所有客户端同时重连,SYN队列瞬间溢出。最后说个反直觉的结论:WebSocket压测的瓶颈往往不在WebSocket本身。我遇到过数据库连接池耗尽、日志同步写磁盘、甚至NTP时间同步导致的延迟抖动。把
顶: 399踩: 75strace挂上去看系统调用,比盯着CPU曲线有用得多。






评论专区