Files
im_server/docs/test/IM-group2k-r9-双机群聊压测分析与改进-20261003.md
T

4.9 KiB
Raw Blame History

GROUP-2000 R9 双机群聊压测分析与改进

1. 结论

group2k-r9-20261003-01 不能作为 2000 人群聊容量结论。消息最终落库完整,但实时阶段同时发生机器 B 迟到、Pod 重启和集群 DNS 拒绝查询,导致大量连接断开和跨机器投递缺失。

  • 发送尝试 2000 条,数据库精确找到 1774 条服务端已确认消息,缺失、重复、字段不一致均为 0。
  • 实时期望投递 3,548,000 次,实际收到 1,384,872 次,到达率约 39.03%。
  • 机器 B 比 coordinator 的计划起跑时间晚约 7 分 55 秒;B 未收到任何 A 消息,本轮并非有效的同步双机负载。
  • im-server-1 在负载中退出,容器退出码 137;现有权限无法读取 Event 和 Pod metrics,尚不能把原因唯一归为 OOM、liveness 或节点压力。
  • 三个 Pod 合计约 1715 次依赖域名解析失败,日志包含对集群 DNS 10.43.0.10:53 的 connection refused。
  • JPush 设备查询产生 792 次数据库错误;用户更新队列满 1132 次,属于依赖抖动和连接重建后的次生压力。

Kafka 持久化链路本轮表现正确:各 Pod 批量持久化计数合计 1774,与数据库精确核验一致,之前的失败消息保留修复已生效。

2. 根因链路

2.1 JPush 查询按群消息放大

JPush 使用 registration ID 模式时,每条群消息都会按全部收件人重新查询 im_device_push_tokens。2000 人群和 2000 条消息会形成约 400 万个用户查询输入;测试账号没有推送设备,旧实现仍重复查询空结果。JPush 回执又以无限 goroutine 并发处理,使查询、对象分配和内存压力同时放大。

2.2 MySQL 连接每 60 秒回收

旧配置的 conn_max_lifetime=60 会在持续压测期间频繁重建连接。每次建连都重新解析 mysql.118im.com;当集群 DNS 短时拒绝请求时,业务查询、JPush 查询和 Kafka 持久化会一起失败或等待。

2.3 双机只有计划时间,没有最终到达屏障

旧协议在双方 READY 后下发统一时间,但 agent 到达该时间后直接发送。机器 B 晚启动时,机器 A 无法知道对方没有真正进入发送阶段,因此形成两段错开的单机流量并生成无效的跨机器统计。

3. 已完成改进

3.1 JPush

  • 设备查询结果按租户、provider、用户缓存 30 秒,包含“无设备”结果。
  • 并发首次查询被合并,同一批群成员只访问一次数据库。
  • 查询失败后退避 1 秒,已有缓存可在 5 分钟陈旧窗口内继续使用。
  • 缓存定期清理,避免用户集合长期增长。
  • JPush 回执限制为 8 个工作协程,取消无限 goroutine,队列负责形成背压。

该改动不删除在线多端静默推送语义。新注册或更新的设备最迟约 30 秒进入推送查询结果。

3.2 MySQL 与 DNS

  • 容器默认 conn_max_lifetime 从 60 秒提高到 1800 秒,并支持 MYSQL_CONN_MAX_LIFETIME_SECONDS 覆盖。
  • 测试环境 GitOps 显式设置 1800 秒,降低持续负载中的连接重建和 DNS 查询频率。
  • 保留现有 ndots:1、timeout:2、attempts:2。

这会显著减少应用制造的 DNS 压力,但不能替代集群 DNS 修复。若复测仍出现 10.43.0.10:53 connection refused,需要集群管理员检查 CoreDNS 副本、CPU、转发上游、conntrack,并评估与当前 K3s 网络模式匹配的 NodeLocal DNSCache;不能在未知网络模式下直接套用 DaemonSet。

3.3 压测同步

  • coordinator 增加最终起跑屏障。
  • A/B 必须在计划时间前后 3 秒内到达,双方都到达后才开始发送。
  • 任一机器迟到时,两边收到同一失败原因并终止本轮。
  • coordinator 输出保存双方实际到达时间和 start_error。

3.4 诊断权限

backend-debug 只读权限增加 Event 和 metrics.k8s.io/pods,用于下一轮区分 OOM、探针失败、驱逐和节点资源压力。若集群未安装 metrics-server,Pod metrics 仍不可用,但 Event 读取不受影响。

4. 复测门禁

下一轮使用新 run_id,A/B 必须更新到包含起跑屏障的同一提交,并在应用和 GitOps 配置都发布后执行。

  1. coordinator 输出中 A/B started 时间相差不超过 3 秒,start_error 为空。
  2. 2000 条发送全部得到明确 ACK;数据库按 client_mid 精确匹配,缺失、重复、字段不一致均为 0。
  3. 实时期望 4,000,000 次投递全部到达,重复、乱序和断连均为 0。
  4. JPush 设备查询不再随群消息数线性增长,日志中 jpush: db error 为 0。
  5. Pod 不重启,Event 中无 OOM、Unhealthy、Evicted;负载窗口内持续采集 CPU、内存和重启次数。
  6. 10.43.0.10:53、MySQL、Redis、Kafka 域名解析错误均为 0。
  7. Kafka lag 最终归零,数据库最终消息数与 ACK 台账一致。

若业务正确性通过但 DNS 仍有拒绝请求,本轮仍判基础设施门禁失败,不继续用增加应用重试掩盖 DNS 容量问题。