160 lines
4.9 KiB
Markdown
160 lines
4.9 KiB
Markdown
# IM 多开跨 Pod 实时投递压测方案
|
||
|
||
日期:2026-09-13
|
||
|
||
## 1. 目标
|
||
|
||
验证 IM Server 使用 Redis realtime fanout 后,在多 Pod、多端登录、群消息和群业务事件场景下的实时投递能力。
|
||
|
||
本次压测重点观察:
|
||
|
||
1. 跨 Pod 在线消息是否能投递到所有目标 WS 连接。
|
||
2. 群消息在多 Pod 下的端到端延迟。
|
||
3. Redis Pub/Sub 发布、接收、投递失败和本地无连接的比例。
|
||
4. Hub 队列满、WS 写队列满、Redis 发布失败等异常是否可观测。
|
||
5. Pod 扩缩容、单 Pod 重启时是否出现明显丢消息或延迟尖刺。
|
||
|
||
## 2. 前置环境
|
||
|
||
建议至少准备:
|
||
|
||
- IM Server:3 个 Pod。
|
||
- Redis:独立实例或 Redis Cluster,开启 `group_fanout_enabled`。
|
||
- MySQL:使用压测租户库。
|
||
- 监控:开启 `/metrics`,采集 Prometheus 指标。
|
||
- 账号:准备足够多的群成员账号 CSV,格式为 `username,password[,token]`。
|
||
- 群 topic:提前创建一个大群,并把压测账号加入该群。
|
||
|
||
每个 Pod 必须配置不同的节点标识:
|
||
|
||
```json
|
||
"node_id": "im-pod-1"
|
||
```
|
||
|
||
或通过环境变量:
|
||
|
||
```shell
|
||
IM_NODE_ID=im-pod-1
|
||
```
|
||
|
||
## 3. 新增服务端指标
|
||
|
||
本次改造新增 Prometheus 指标:
|
||
|
||
```text
|
||
realtime_fanout_events_total{stage,kind,target_type,status}
|
||
realtime_fanout_delivery_duration_seconds_bucket{kind,target_type}
|
||
```
|
||
|
||
常用查询:
|
||
|
||
```promql
|
||
sum by (stage, kind, target_type, status) (rate(realtime_fanout_events_total[1m]))
|
||
```
|
||
|
||
```promql
|
||
histogram_quantile(
|
||
0.95,
|
||
sum by (le, kind, target_type) (rate(realtime_fanout_delivery_duration_seconds_bucket[5m]))
|
||
)
|
||
```
|
||
|
||
重点关注 status:
|
||
|
||
- `publish/ok`:本 Pod 成功发布到 Redis。
|
||
- `publish/error`:Redis 发布失败。
|
||
- `publish/skip_native_cluster`:启用了 Tinode 原生 cluster,跳过 Redis topic fanout。
|
||
- `receive/ok`:其他 Pod 收到 Redis fanout。
|
||
- `receive/malformed`:收到无法解析的消息。
|
||
- `deliver/local_delivered`:本 Pod 找到本地 WS 并投递。
|
||
- `deliver/no_local_session`:本 Pod 没有目标本地连接,属于正常情况。
|
||
- `deliver/hub_full`:Hub routeSrv 队列满,需要重点告警。
|
||
- `deliver/skip_no_session_store`:session store 未初始化。
|
||
|
||
## 4. 压测脚本
|
||
|
||
脚本位置:
|
||
|
||
```shell
|
||
loadtest/realtime_fanout_probe.go
|
||
```
|
||
|
||
运行示例:
|
||
|
||
```shell
|
||
go run ./loadtest/realtime_fanout_probe.go \
|
||
-ws "ws://im-pod-1.example.com/v0/channels,ws://im-pod-2.example.com/v0/channels,ws://im-pod-3.example.com/v0/channels" \
|
||
-apikey "$IM_API_KEY" \
|
||
-accounts loadtest/users.csv \
|
||
-topic grpXXXXXXXXXXXX \
|
||
-subscribers 3000 \
|
||
-publishers 10 \
|
||
-rate 200 \
|
||
-duration 10m \
|
||
-ramp 2m
|
||
```
|
||
|
||
脚本行为:
|
||
|
||
1. 按 CSV 账号建立 WS 连接。
|
||
2. 将连接均匀分布到 `-ws` 提供的多个 Pod 地址。
|
||
3. 每个连接登录并订阅同一个群 topic。
|
||
4. 从前 `-publishers` 个连接中选择发布者。
|
||
5. 按 `-rate` 总速率发布带 `probe_id` 和 `sent_at_unix_nano` 的群消息。
|
||
6. 所有订阅连接统计收到的 `{data}`,输出接收数、丢失率、p50/p95/p99/max 延迟。
|
||
|
||
输出示例:
|
||
|
||
```text
|
||
==== realtime fanout probe summary ====
|
||
published=120000 active_subscribers=3000 expected_deliveries=360000000 received=359998200 lost=1800 loss_rate=0.0005% unique_messages_seen=120000 errors=0 ctrl_acks=120000
|
||
latency p50=18ms p95=65ms p99=140ms max=900ms samples=359998200
|
||
```
|
||
|
||
## 5. 压测阶梯
|
||
|
||
建议按下面阶梯逐步推进,每个阶梯至少跑 10 分钟:
|
||
|
||
| 阶段 | Pod | 在线连接 | 发布者 | 总 QPS | 目标 |
|
||
| --- | --- | ---: | ---: | ---: | --- |
|
||
| S1 | 3 | 300 | 1 | 10 | 验证链路正确性 |
|
||
| S2 | 3 | 3000 | 10 | 100 | 验证常规群聊压力 |
|
||
| S3 | 3 | 10000 | 20 | 300 | 验证大群在线 fanout |
|
||
| S4 | 6 | 30000 | 50 | 500 | 验证扩容后的 Redis fanout |
|
||
| S5 | 6 | 30000 | 50 | 1000 | 探测上限和队列瓶颈 |
|
||
|
||
## 6. 通过标准
|
||
|
||
建议初始通过标准:
|
||
|
||
- `publish/error` 为 0。
|
||
- `receive/malformed` 为 0。
|
||
- `deliver/hub_full` 为 0。
|
||
- 压测脚本统计丢失率低于 0.01%。
|
||
- p95 端到端延迟低于 200ms。
|
||
- p99 端到端延迟低于 500ms。
|
||
- Pod CPU、内存、GC 无持续上升趋势。
|
||
- Redis CPU 不超过 70%,网络出入带宽有余量。
|
||
|
||
## 7. 故障注入
|
||
|
||
基础压测通过后执行:
|
||
|
||
1. 压测中重启一个 IM Pod,观察其他 Pod 是否继续投递。
|
||
2. 压测中扩容 Pod 数,观察 `receive/ok` 是否按 Pod 数增加。
|
||
3. 临时降低 Hub/WS 队列容量或制造慢客户端,确认 `hub_full`、WS 写失败日志可见。
|
||
4. Redis 短暂不可用,确认 `publish/error` 增加,恢复后新消息继续投递。
|
||
|
||
## 8. 结果记录
|
||
|
||
每轮记录:
|
||
|
||
- Git commit。
|
||
- IM Pod 数量和规格。
|
||
- Redis 规格。
|
||
- 在线连接数、发布 QPS、消息大小。
|
||
- 脚本输出摘要。
|
||
- Prometheus p95/p99。
|
||
- `publish/error`、`deliver/hub_full`、WS 写失败数量。
|
||
- Pod 重启/扩容期间的最大延迟和丢失率。
|