2026-07-01 · 独立脚本验证,不依赖 blade-agent 真实代码
预压测显示:50 并发正常,100 并发(50 用户 × 2 任务)成功率仅 39/100,失败全部是 Socket.IO 连接握手失败。 压测期间 CPU 峰值仅 37%、内存 8.9/12.9G、8020 端口连接数峰值只到 68——不是硬件资源瓶颈。
同事的初步结论:根因是 blade-agent 后端单进程单 asyncio 事件循环处理 Socket.IO 握手的能力上限。 本报告通过独立最简脚本验证这个假设。
均为 python-socketio AsyncServer + ASGI + uvicorn 单进程,与 blade-agent 配置一致
await asyncio.sleep(0.05),模拟 50ms 异步 DB/auth 查询
time.sleep(0.05),模拟 50ms 同步文件/DB 操作冻住事件循环
| 服务端 | 客户端 | 50 | 100 | 200 | 500 |
|---|---|---|---|---|---|
| 最简服务端 | Python asyncio | 100% | 100% | 100% | 100% |
| Go zishang520 | 14% | 14% | 8% | 13% | |
| +50ms 异步 IO | Python asyncio | 100% | 100% | 100% | 100% |
| +50ms 同步阻塞 | Python asyncio | 2% | 1% | 0% | 2% |
| 场景 | 并发 | min | p50 | p95 | max | 总耗时 |
|---|---|---|---|---|---|---|
| 最简 + Python | 50 | 20ms | 25ms | 25ms | 26ms | 0.03s |
| 100 | 60ms | 69ms | 72ms | 73ms | 0.08s | |
| 200 | 81ms | 98ms | 100ms | 101ms | 0.11s | |
| 500 | 240ms | 270ms | 294ms | 297ms | 0.32s | |
| 异步 IO + Python | 50 | 79ms | 81ms | 83ms | 83ms | 0.08s |
| 100 | 110ms | 116ms | 119ms | 120ms | 0.12s | |
| 200 | 129ms | 141ms | 150ms | 150ms | 0.15s | |
| 500 | 263ms | 294ms | 316ms | 318ms | 0.33s | |
| 同步阻塞 + Python | 500 | 3194ms | 3194ms | 9797ms | 9797ms | 15.01s |
纯 Socket.IO 协议栈(python-socketio AsyncServer + ASGI)在单进程单事件循环下可轻松处理 500 并发握手, 成功率 100%,最大延迟仅 297ms。即使 connect 中有 50ms 异步 IO 也完全不影响。 Socket.IO 本身不是瓶颈。
一旦 connect 处理器中存在 同步阻塞操作(同步文件读写、同步 DB 查询、CPU 密集计算), 仅 50ms 的阻塞就能让 50 并发成功率暴跌至 2%——完美复现了预压测中 39% 成功率的现象。 同步调用冻住了事件循环,所有其他连接请求排队串行等待。
即使面对最简的空 connect 服务端,github.com/zishang520/socket.io 客户端在 50 并发下成功率仅 14%,
绝大部分连接超时。如果压测工具使用了该库,需注意失败可能来自客户端侧。
connect 和 session:subscribe 处理链路,
grep 同步阻塞调用:time.sleep、同步 open()、
sqlite3 同步查询、subprocess.run 等
await asyncio.to_thread() 卸载到线程池;
长耗时操作考虑异步库替代(如 aiosqlite、aiofiles)
--workers N),但前提是先消除同步阻塞——否则多进程只是线性提升,不解决根因
测试环境:macOS Darwin 25.3.0 · Python 3.9 + python-socketio 5.16.3 + uvicorn 0.39.0 · Go 1.22 + zishang520/socket.io v3.0.0