# IM 单机 60% 容量压力测试方案 日期:2026-09-29 本方案首轮执行结果见 [IM-单机60%容量压力测试结果-20260929.md](./IM-单机60%容量压力测试结果-20260929.md)。 ## 1. 目标与口径 本方案用一台物理压测机完成单聊、群聊和混合场景,容量按双机正式方案全局规模的 60% 计算。 单机不等于只运行一个 agent。为保留现有好友对和投递契约,同一台物理机上同时运行逻辑 agent A 和 agent B,每个 agent 承载 600 个连接,合计 1200 个 WebSocket。Coordinator 仍使用 `expected-agents=2`。 | 项目 | 双机正式方案 | 单机 60% 方案 | |---|---:|---:| | 物理压测机 | 2 | 1 | | 逻辑 agent | 2 | 2 | | 每个 agent 连接 | 1000 | 600 | | WebSocket 总连接 | 2000 | 1200 | | 好友对 | 1000 | 600 | | 群成员 | 2000 | 1200 | ## 2. 场景容量 ### 2.1 单聊 - 600 对好友,A/B 各 600 人。 - 每个用户发送 10 条,每秒 1 条。 - 总发送尝试 12000 条,是双机 20000 条的 60%。 - 每条 ACK 消息应在配对用户的 WebSocket 到达一次。 ### 2.2 群聊 - 1200 个在线群成员。 - A/B 各取前 300 个用户作为发送者,合计 600 个发送者。 - 每人发送 2 条,全局每 100ms 一条,合计 1200 条群消息,耗时约 120 秒。 - 全部发送被 ACK 时,期望实时投递 `1200 x 1200 = 1,440,000` 次。 ### 2.3 单聊和群聊混合 - 前 180 对好友互发,每人 5 条,共 1800 条单聊。 - A/B 各 150 个群发送者,每人 2 条,共 600 条群消息。 - 群消息期望实时投递 `600 x 1200 = 720,000` 次。 - 单聊和群聊同时开始,不分两轮单独执行。 工具按 `users_per_machine` 等比计算发送者数量。`users_per_machine=1000` 时保持原双机参数,设为 600 时自动使用上述单机参数。 ## 3. 文件隔离 单机方案不复用双机的运行文件,避免把 1200 人结果和 2000 人结果混在一起。 ```text loadtest/im-pressure/config.single-machine.json loadtest/im-pressure/manifest.single-machine.json loadtest/im-pressure/results// ``` 从模板生成本地配置: ```bash cp loadtest/im-pressure/config.single-machine.example.json \ loadtest/im-pressure/config.single-machine.json chmod 600 loadtest/im-pressure/config.single-machine.json ``` 修改租户、HTTP/WS 地址和注册手机前缀。A/B endpoint 可以填同一个公网入口;若需确认跨 Pod 投递,应分别填写两个定向入口。 ## 4. 一次性数据准备 ```bash source loadtest/im-pressure/.env.test go build -o loadtest/im-pressure/im-pressure ./loadtest/im-pressure ulimit -n 100000 ./loadtest/im-pressure/im-pressure prepare \ -config loadtest/im-pressure/config.single-machine.json \ -manifest loadtest/im-pressure/manifest.single-machine.json ./loadtest/im-pressure/im-pressure verify \ -config loadtest/im-pressure/config.single-machine.json \ -manifest loadtest/im-pressure/manifest.single-machine.json ``` `verify` 必须输出 1200 个账号、600 对好友和 1200 个群成员。单机 manifest 可以复用已注册的 `lta0001..0600` 和 `ltb0001..0600` 账号,但必须使用独立群 Topic。 ## 5. 正式运行 以群聊为例: ```bash export RUN_ID='single60-group-r1-YYYYMMDD-01' export SCENARIO='group' mkdir -p "loadtest/im-pressure/results/${RUN_ID}" ``` 终端 1,启动 coordinator: ```bash ./loadtest/im-pressure/im-pressure coordinator \ -listen :8090 \ -run-id "${RUN_ID}" \ -scenario "${SCENARIO}" \ -expected-agents 2 \ -output "loadtest/im-pressure/results/${RUN_ID}/coordinator.json" ``` 终端 2,启动逻辑 agent A: ```bash ./loadtest/im-pressure/im-pressure agent \ -config loadtest/im-pressure/config.single-machine.json \ -manifest loadtest/im-pressure/manifest.single-machine.json \ -machine a \ -coordinator http://127.0.0.1:8090 \ -metrics-listen :9105 \ -output "loadtest/im-pressure/results/${RUN_ID}/agent-a-summary.json" ``` 终端 3,启动逻辑 agent B: ```bash ./loadtest/im-pressure/im-pressure agent \ -config loadtest/im-pressure/config.single-machine.json \ -manifest loadtest/im-pressure/manifest.single-machine.json \ -machine b \ -coordinator http://127.0.0.1:8090 \ -metrics-listen :9106 \ -output "loadtest/im-pressure/results/${RUN_ID}/agent-b-summary.json" ``` 两个 agent 都报告 `600/600 READY` 并稳定通过 30 秒屏障后才会开始。任一 agent 未满连接时 coordinator 会撤销开始时间。 ## 6. 指标采集 终端 4 在起压前至少 2 分钟启动: ```bash ./loadtest/im-pressure/im-pressure collect \ -duration 20m -interval 5s \ -coordinator http://127.0.0.1:8090 \ -coordinator-output "loadtest/im-pressure/results/${RUN_ID}/coordinator.json" \ -url agent-a=http://127.0.0.1:9105/metrics \ -url agent-b=http://127.0.0.1:9106/metrics \ -url node=http://127.0.0.1:9100/metrics \ -output "loadtest/im-pressure/results/${RUN_ID}/metrics.jsonl" ``` 同时采集 Redis、Kafka、MySQL、Pod CPU/内存/网络、Kafka consumer lag、MySQL 连接池等指标。如本机没有 `node-exporter :9100`,删除 `node` URL,但不影响 agent 指标采集。 ## 7. 报告和独立落库核验 ```bash ./loadtest/im-pressure/im-pressure merge \ -a "loadtest/im-pressure/results/${RUN_ID}/agent-a-summary.json" \ -b "loadtest/im-pressure/results/${RUN_ID}/agent-b-summary.json" \ -output "loadtest/im-pressure/results/${RUN_ID}/report.json" export MYSQL_DSN='readonly:password@tcp(mysql-host:3306)/meeting_im?parseTime=true' export TENANT='006' # 必须与 config.single-machine.json 中的 tenant 一致 ./loadtest/im-pressure/im-pressure verify-db \ -coordinator "loadtest/im-pressure/results/${RUN_ID}/coordinator.json" \ -tenant "${TENANT}" \ -dsn-env MYSQL_DSN \ -registry-db hotgo \ -output "loadtest/im-pressure/results/${RUN_ID}/db-verification.json" ``` ## 8. 验收门禁 - READY 必须是 `1200/1200`,统计窗口内断连为 0。 - publish 明确拒绝和结果未知均为 0,成功率至少 99.99%。 - ACK P95 不高于 500ms,同机投递 P95 不高于 1000ms。 - 实时缺失、重复、乱序、未知本轮消息均为 0。 - ACK 台账和无 ACK 但已观测台账的历史回查命中率必须 100%,独立 MySQL 核验丢失、错配、重复均为 0。 - 实时发送、历史回查和渐进关闭三个阶段分别统计 Topic/Proxy/Session 队列满,不能只看整轮总数。 ## 9. 单机特有风险 1. 两个 agent 共用一台机器的 CPU、内存、FD、NAT 和出口带宽,测到的是“单机压测端 + 服务端”综合上限,不能代替双机最终验收。 2. 正式前做 120/600/1200 连接三档阶梯,观察 agent CPU、`go_goroutines`、FD、接收速率和 Ping/Pong 断连,确认压测端本身没有先饱和。当前工具已将投递观测按 `client_mid` 分片、接收账号顺序状态独立加锁,并使用原子延迟直方图;仍需用指标证明本机有余量。 3. 两个 agent 必须使用不同指标端口,不能同时绑定 `:9105`。 4. 压测域名如被 VPN/TUN 接管,起压前必须确认所有当前 IP 的路由都指向物理网卡。 5. 单机 60% 通过后只能进入双机 100% 验收,不能直接得出 2000 连接容量通过的结论。 断连时同时查看 agent summary 中的 `disconnect_reasons` 和 `disconnect_samples`。前者按 timeout、abnormal close、connection reset 等原因分类计数,后者最多保留 100 条带账号与时间的原始错误样本,用于区分压测端读循环阻塞、网络中断和服务端主动关闭。