Files
im_server/docs/IM-多开跨Pod实时投递压测方案.md
T

160 lines
4.9 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 多开跨 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 重启/扩容期间的最大延迟和丢失率。