WebSocket压测卡在1k连接是因Linux默认文件描述符限制为1024,突破后触发“Too many open files”导致静默丢连;需调高ulimit、内核参数,并用websockets库实现连接池复用。

为什么 WebSocket 压测卡在 1k 连接就上不去
不是代码写得烂,是每个连接默认独占一个 TCP socket + 文件描述符(fd),而 Linux 默认 ulimit -n 是 1024。一旦突破,Too many open files 就开始静默丢连接,JMeter 线程组里全是红叉,日志里却只报 java.net.ConnectException —— 实际根本没发出去。
临时解决:启动压测脚本前先执行 ulimit -n 65535;永久生效要改 /etc/security/limits.conf,加两行:* soft nofile 1048576 和 * hard nofile 1048576,再确认你的 JMeter 或 Python 进程是用该用户启的。
- 别漏掉内核参数:
net.core.somaxconn = 65535、net.ipv4.tcp_tw_reuse = 1,写进/etc/sysctl.conf后sysctl -p - Python 多进程 + 多线程混合压测时,每个子进程都得单独调
ulimit,不能只在父进程设 - JMeter 启动脚本(如
jmeter.sh)里要显式加ulimit -n 65535,否则 GUI 或 CLI 模式都无效
Python websocket 客户端怎么复用连接做池化
原生 websocket-client 库不带连接池,WebSocketApp 是“一连接一实例”模型,每建一个连接就 new 一个对象,无法复用底层 socket。真要池化,得换库或自己包一层。
推荐直接用 websockets 库(注意不是 websocket-client),它底层基于 asyncio,天然支持连接复用逻辑:
import asyncio
import websockets
<h1>创建连接池:预热 100 个连接并缓存</h1><p>_pool = []</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/gongju/2287" title="WebSocket 8.18.2"><img
src="https://img.php.cn/upload/manual/001/221/864/6a1560869301b447.png" alt="WebSocket 8.18.2" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/gongju/2287" title="WebSocket 8.18.2">WebSocket 8.18.2</a>
<p>WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。</p>
</div>
<a href="/xiazai/gongju/2287" title="WebSocket 8.18.2" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><p>async def init<em>pool(uri, size=100):
for </em> in range(size):
try:
ws = await websockets.connect(uri, compression=None)
_pool.append(ws)
except Exception:
pass</p><p>async def get_ws():
if _pool:
return _pool.pop()
return await websockets.connect(uri, compression=None)</p><p>async def release_ws(ws):
if len(_pool) < 100: # 限制最大池大小
_pool.append(ws)
else:
await ws.close()</p>-
compression=None必加,单连接内存从 ~64KB 降到 ~14KB,5000 连接就能省下近 250MB - 池里连接必须主动
ping心跳保活,否则 Nginx 默认 60 秒断连,proxy_read_timeout得设成 86400 - 不要把
websockets.connect()放在同步线程里调用——会阻塞整个 event loop,必须用asyncio.to_thread包或改用anyio
JMeter 怎么让 WebSocket Sampler 复用连接
JMeter 原生不支持 WebSocket,靠插件(如 WebSocket Samplers by Peter Doornbosch)实现,但它默认仍是“每线程每采样器新建连接”,高并发下 fd 耗尽、GC 压力大、连接建立耗时占比飙升。
真正能复用连接的方式只有一种:自定义 Java Sampler,继承 AbstractJavaSamplerClient,手动管理 WebSocketClient 实例生命周期:
// 在 setupTest() 里初始化连接池 client.getConnectionManager().setMaxConnectionsPerHost(500); client.getConnectionManager().setKeepAliveTimeout(30000); <p>// 在 runTest() 里从池取连接,发完不 close,归还 WebSocketSession session = client.openWebSocket(uri); session.send(message); session.flush(); // 不调 session.close(),交给池回收</p>
- 必须关掉插件自带的“Connect on each sample”选项,否则池白建
- 连接池要按 host:port 维度隔离,不同服务端地址不能混用同一池
- 如果压测含认证(如 JWT 放 header),得在每次
send()前动态注入 token,不能只在 connect 阶段设一次
连接池没用好反而更慢?三个典型坑
池化不是加个 queue.Queue 就完事。很多压测脚本表面用了池,实际性能比直连还差,问题往往出在这三处:
- 池大小设成 5000,但服务端
max_connections只配了 1000,大量连接卡在 TCP SYN 队列,客户端超时重试,反而放大延迟 - 没做连接健康检查,池里混入已断开的 socket(比如服务端主动踢人后未通知),后续
send()直接抛BrokenPipeError,错误率飙升 - Python 用
threading.Lock保护池操作,但锁粒度太粗——每次get()+release()都要抢锁,1000 线程争一个锁,CPU 全花在上下文切换上
真实压测中,连接池的“有效并发”常只有理论值的 60%–80%,关键不在数量,而在连接是否活跃、是否低延迟、是否被服务端接纳。别盯着数字刷高,先看 ss -s 里 established 数和 time_wait 分布,再看服务端 netstat -an | grep :端口 | wc -l 是否匹配。

















