Files
im_server/docs/test/IM-单机60%容量压力测试方案.md
T

7.8 KiB
Raw Blame History

IM 单机 60% 容量压力测试方案

日期:2026-09-29

本方案首轮执行结果见 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 人结果混在一起。

loadtest/im-pressure/config.single-machine.json
loadtest/im-pressure/manifest.single-machine.json
loadtest/im-pressure/results/<RUN_ID>/

从模板生成本地配置:

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. 一次性数据准备

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. 正式运行

以群聊为例:

export RUN_ID='single60-group-r1-YYYYMMDD-01'
export SCENARIO='group'
mkdir -p "loadtest/im-pressure/results/${RUN_ID}"

终端 1,启动 coordinator:

./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:

./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:

./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 分钟启动:

./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 指标采集。

发版后必须在服务端 /metrics 确认 im_ws_heartbeat_seconds{setting="ping_period"}=15、im_ws_heartbeat_seconds{setting="pong_wait"}=90,并采集 im_ws_heartbeat_total{event="pong_received"} 和 im_ws_disconnect_total{reason="timeout"} 的分 Pod 增量。

7. 报告和独立落库核验

./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 分片、接收账号顺序状态独立加锁,使用原子延迟直方图,并将数据统计回调从 WebSocket socket 读循环解耦;仍需用指标证明本机有余量。
  3. 两个 agent 必须使用不同指标端口,不能同时绑定 :9105。
  4. 压测域名如被 VPN/TUN 接管,起压前必须确认所有当前 IP 的路由都指向物理网卡。
  5. 单机 60% 通过后只能进入双机 100% 验收,不能直接得出 2000 连接容量通过的结论。

断连时同时查看 agent summary 中的 disconnect_reasons 和 disconnect_samples。前者按 timeout、abnormal close、connection reset 等原因分类计数,后者最多保留 100 条带账号与时间的原始错误样本,用于区分压测端读循环阻塞、网络中断和服务端主动关闭。