Files
im_server/loadtest/im-pressure/README.md
T

262 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IM 双机压力测试工具
该工具按配置指定的企业租户运行:两台压测机各承载 1000 个 WebSocket 用户,共 2000 个用户、1000 对好友和一个 2000 人群。当前测试环境使用 `allrich`,本轮实时参数见 [MACHINE-B.md](./MACHINE-B.md) 和 [allrich 双机压测执行记录](../../docs/test/IM-allrich双机压测执行记录-20260927.md)。
机器 B 上的 AI 或操作人请直接阅读 [MACHINE-B.md](./MACHINE-B.md)。该文件不重复讲方案,只给出机器 B 从核验到启动、观察和回传结果的完整命令。
## 两台机器的固定分工
当前仓库所在机器固定作为 **机器 A**。两台机器不要交换 `-machine` 参数,否则账号分片和结果归属会混乱。
| 项目 | 机器 A(当前机器,主控) | 机器 B(另一台机器,发压) |
|---|---|---|
| 模拟账号 | `lta0001` 至 `lta1000` | `ltb0001` 至 `ltb1000` |
| WS 连接数 | 1000 | 1000 |
| 固定参数 | `-machine a` | `-machine b` |
| 数据准备 | 执行 `prepare`、`verify` | 不执行 |
| 双机协调 | 运行 `coordinator :8090` | 连接机器 A 的 `:8090` |
| 场景发压 | 运行 agent A | 运行 agent B |
| 指标 | agent `:9105`,运行中央 `collect` | agent `:9105`,供机器 A 抓取 |
| 结果 | 保存 A 结果、接收 B 结果、执行 `merge` | 保存 B 结果并传回机器 A |
机器 A 至少准备三个终端:协调器、agent A、指标采集。机器 B 至少准备一个终端运行 agent B;需要单独采集本机指标时再开第二个终端。
### 机器 B 最短操作清单
机器 B 的完整命令见 [MACHINE-B.md](./MACHINE-B.md)。机器 B 操作人只需完成以下事项:
1. 检出与机器 A 相同的 Git 提交并构建 `im-pressure`。
2. 接收机器 A 生成的 `config.json` 和 `manifest.json`,不要自行运行 `prepare`。
3. 设置与机器 A 相同的 `IM_API_KEY`、`IM_LOAD_PASSWORD`,执行一次 `verify`。
4. 从机器 A 获取本轮 `RUN_ID` 和机器 A 内网 IP。
5. 等机器 A 启动 coordinator 后,固定使用 `-machine b` 启动 agent B。
6. agent B 退出后,将 `agent-b-summary.json` 传回机器 A。
机器 B 不负责建账号、加好友、建群、选择场景或合并报告。
### 网络要求
- 机器 B 必须能访问 `http://机器A内网IP:8090`。
- 如果由机器 A 集中采集 agent 指标,机器 A 必须能访问 `http://机器B内网IP:9105/metrics`。
- 两台机器都必须能访问配置中的 HTTP API 和各自的 WS 地址。
- `8090` 是无鉴权的压测协调端口,只允许压测内网访问,不要暴露到公网。
- 两台机器必须使用 NTP/chrony 同步时间;端到端延迟依赖两边时钟一致。
## 统计口径
- 发送成功以 `{ctrl code=2xx, params.seq>0}` 为准,形成两台机器共享的 ACK 台账。
- 实时到达只统计同时匹配本轮 `run_id` 和 `x-im-client-mid` 的 `{data}`。
- 群消息设置 `noecho=false`,每条服务端接受的群消息期望 2000 个连接各收到一次。
- 实时 drain 结束后通过 `{get what=data}` 按 ACK 的 Topic/Seq 回查历史,区分跨 Pod 实时漏投与数据库持久化缺失。
- 每轮建立 WS 后、正式计时前,以 32 个 worker 对本机 1000 个账号做好友关系、P2P Topic 和发布权限预检;任一异常会终止本轮。
- 历史回查默认使用 48 个 worker、总超时 3 分钟,并对缺失项再并发重试两轮。
- publish 错误完整分类为 `403`、`502`、`503`、`ctrl_timeout`、`socket_error` 和 `other`,不受文本错误样本上限影响。
- `manifest.json` 含登录 token,文件权限为 `0600`,不得提交到 Git 或放入结果包。
## 两台机器初始化
两台机器先检出完全相同的 Git 提交,并分别构建本机二进制:
```bash
git rev-parse HEAD
go build -o loadtest/im-pressure/im-pressure ./loadtest/im-pressure
ulimit -n 100000
```
只在机器 A 创建配置:
```bash
cp loadtest/im-pressure/config.example.json loadtest/im-pressure/config.json
export IM_API_KEY='...'
export IM_LOAD_PASSWORD='...'
```
修改 `config.json`:`http_base` 指向 IM HTTP API;`endpoints.a/b` 分别填写两台机器应直连的 WS Pod/LB 地址,多个地址时用户会轮询分配。正式测试保持 `insecure_tls=false`。
测试环境要求短信验证注册时,将 `registration_phone_prefix` 配置为 7 位数字。工具为机器 A 追加后缀 `0001` 起的序号,为机器 B 追加后缀 `5001` 起的序号,生成互不冲突的 11 位测试手机号;验证码从 `verification_code_env` 指定的环境变量读取。正式环境不得使用固定测试验证码。
`endpoints.a` 是机器 A 的 1000 个用户使用的 WS Pod/LB 地址,`endpoints.b` 是机器 B 的 1000 个用户使用的地址。要验证跨 Pod 投递时,应让 A、B 连接到不同 Pod 或不同的定向入口;如果都填同一个普通 LB,连接落在哪个 Pod 将由 LB 决定。
机器 B 同样需要设置 `IM_API_KEY` 和 `IM_LOAD_PASSWORD`。密码用于 Token 失效时自动回退 Basic 登录,不能只在机器 A 设置。
## 机器 A:一次性准备
以下命令只在当前机器 A 执行。命令可重复运行,会复用已有账号、好友和群:
```bash
./loadtest/im-pressure/im-pressure prepare \
-config loadtest/im-pressure/config.json \
-manifest loadtest/im-pressure/manifest.json
./loadtest/im-pressure/im-pressure verify \
-config loadtest/im-pressure/config.json \
-manifest loadtest/im-pressure/manifest.json
```
准备成功后,机器 A 的 manifest 中包含 2000 个账号 Token、1000 个 P2P Topic 和群 Topic。将配置和 manifest 安全复制到机器 B:
```bash
export MACHINE_B_SSH='user@机器B内网IP'
export MACHINE_B_REPO='/home/user/im_server'
scp loadtest/im-pressure/config.json \
"${MACHINE_B_SSH}:${MACHINE_B_REPO}/loadtest/im-pressure/config.json"
scp loadtest/im-pressure/manifest.json \
"${MACHINE_B_SSH}:${MACHINE_B_REPO}/loadtest/im-pressure/manifest.json"
```
机器 B 收到后执行:
```bash
cd /home/user/im_server
chmod 600 loadtest/im-pressure/manifest.json
export IM_API_KEY='...'
export IM_LOAD_PASSWORD='...'
./loadtest/im-pressure/im-pressure verify \
-config loadtest/im-pressure/config.json \
-manifest loadtest/im-pressure/manifest.json
```
`MACHINE_B_REPO` 必须填写机器 B 上仓库的绝对路径。每次机器 A 重新执行 `prepare` 并改变了 manifest,都必须重新同步到机器 B。
## 每轮双机运行
以下示例执行单聊场景。机器 A 设置本轮场景;两台机器使用同一个、每轮唯一且不超过 24 个字符的 `RUN_ID`,保证结果目录一致:
```bash
export RUN_ID='p2p-20260927-01'
export SCENARIO='p2p'
export MACHINE_A_IP='机器A内网IP'
export MACHINE_B_IP='机器B内网IP'
mkdir -p "loadtest/im-pressure/results/${RUN_ID}"
```
机器 A 应在正式起压前启动终端 A3 的指标采集,然后按 A1、A2、B1 的顺序启动协调器和两个 agent。
### 第一步:机器 A 启动协调器
机器 A 终端 A1:
```bash
./loadtest/im-pressure/im-pressure coordinator \
-listen :8090 \
-run-id "${RUN_ID}" \
-scenario "${SCENARIO}" \
-output "loadtest/im-pressure/results/${RUN_ID}/coordinator.json"
```
协调器会等待两个 agent 都报告 1000 条连接 READY,再统一下发至少 30 秒后的开始时间。不要跳过协调器直接启动 agent。
### 第二步:机器 A 启动 agent A
机器 A 终端 A2:
```bash
./loadtest/im-pressure/im-pressure agent \
-config loadtest/im-pressure/config.json \
-manifest loadtest/im-pressure/manifest.json \
-machine a \
-coordinator "http://${MACHINE_A_IP}:8090" \
-metrics-listen :9105 \
-output "loadtest/im-pressure/results/${RUN_ID}/agent-a-summary.json"
```
### 第三步:机器 B 启动 agent B
机器 B 终端 B1 需要先设置与机器 A 相同的值:
```bash
cd /home/user/im_server
export IM_API_KEY='...'
export IM_LOAD_PASSWORD='...'
export RUN_ID='p2p-20260927-01'
export MACHINE_A_IP='机器A内网IP'
mkdir -p "loadtest/im-pressure/results/${RUN_ID}"
./loadtest/im-pressure/im-pressure agent \
-config loadtest/im-pressure/config.json \
-manifest loadtest/im-pressure/manifest.json \
-machine b \
-coordinator "http://${MACHINE_A_IP}:8090" \
-metrics-listen :9105 \
-output "loadtest/im-pressure/results/${RUN_ID}/agent-b-summary.json"
```
机器 B 不需要传 `SCENARIO`,场景由机器 A 的 coordinator 下发。两个 agent 可以任意先后启动,但 coordinator 必须最先启动。
两个 agent 都建立 1000 条连接后,协调器下发统一的 UTC `start_at`。场景可替换为:
- `p2p`:1000 对用户两侧同时发送,默认每人 10 条、间隔 1 秒。
- `group`:每台机器 500 个发送者,每人 2 条;全局每 100ms 一条,共 2000 条。
- `mixed`:300 对私聊用户每人 5 条,同时每台 250 个群发送者各发 2 条。
## 机器 A:回收并合并报告
两个 agent 完成历史回查并退出后,在机器 A 拉取机器 B 的 summary:
```bash
scp "${MACHINE_B_SSH}:${MACHINE_B_REPO}/loadtest/im-pressure/results/${RUN_ID}/agent-b-summary.json" \
"loadtest/im-pressure/results/${RUN_ID}/agent-b-summary.json"
```
然后仍在机器 A 合并报告:
```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"
```
`report.json` 给出 ACK、实时投递、重复、乱序、断连和历史持久化核验结果。当前正确性门禁要求上述错误计数全部为 0。
每个 agent 在 `:9105/metrics` 暴露连接、发送、接收、重复、乱序、断连以及 Go/process 指标;端口冲突时可通过 `-metrics-listen` 修改,传空字符串可关闭。
## 机器 A:集中采集指标
`collect` 可并行抓取 Prometheus、Redis exporter、Kafka exporter、mysqld exporter、kube-state-metrics/cAdvisor 和压测机 node-exporter,原始响应按时间写入 JSONL:
```bash
./loadtest/im-pressure/im-pressure collect \
-duration 20m -interval 5s \
-coordinator "http://${MACHINE_A_IP}: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://${MACHINE_B_IP}:9105/metrics \
-url im=http://PROMETHEUS_HOST/api/v1/query?query=up \
-url redis=http://REDIS_EXPORTER/metrics \
-url kafka=http://KAFKA_EXPORTER/metrics \
-url mysql=http://MYSQLD_EXPORTER/metrics \
-url node-a=http://NODE_A_EXPORTER/metrics \
-url node-b=http://NODE_B_EXPORTER/metrics \
-output "loadtest/im-pressure/results/${RUN_ID}/metrics.jsonl"
```
机器 A 终端 A3 运行采集器。每条 JSONL 快照会自动标记 `phase=before|during|after`:coordinator 下发开始时间后进入 `during`,coordinator 结果文件生成后进入 `after`。URL 含 `&` 时需用单引号包住整个 `name=url` 参数。建议起压前 2 分钟开始采集,结束后继续 5 分钟,以观察 Kafka lag、数据库落库追平时间及资源恢复。如果机器 A 无法访问机器 B 的 `9105`,就在机器 B 单独运行一份 `collect`,结束后把 JSONL 一并传回机器 A。
## MySQL 独立落库核验
合并报告后在机器 A 使用只读 DSN 对 ACK ledger 做独立校验。工具会根据租户和消息时间自动查询 `im_NNN_messages` 及涉及的群消息月表,精确比较 `client_mid + topic + seq`,并统计缺失、错配、重复和非 ACK 消息:
```bash
export MYSQL_DSN='readonly:password@tcp(mysql-host:3306)/meeting_im?parseTime=true'
./loadtest/im-pressure/im-pressure verify-db \
-coordinator "loadtest/im-pressure/results/${RUN_ID}/coordinator.json" \
-tenant allrich \
-dsn-env MYSQL_DSN \
-registry-db hotgo \
-output "loadtest/im-pressure/results/${RUN_ID}/db-verification.json"
```
DSN 只能通过环境变量传入,不要写入配置、命令脚本或结果包。命令在 missing、mismatch、duplicate 或 unexpected 任一计数非零时返回失败。
## 运行前检查
```bash
ulimit -n 100000
curl -fsS "http://${MACHINE_A_IP}:8090/v1/status"
curl -fsS http://127.0.0.1:9105/metrics | head
```
两台压测机应完成时钟同步,并确认压测出口、Ingress、Pod 与 Redis/MySQL/Kafka 连接数限制可以容纳本轮负载。协调器没有鉴权,只能监听在受控压测网络中。