fix(group): bound push pressure and synchronize load agents
This commit is contained in:
@@ -737,7 +737,7 @@ loadtest/im-pressure/internal/collector/
|
||||
loadtest/im-pressure/internal/report/
|
||||
```
|
||||
|
||||
协调方式:Machine A 启动 coordinator,等待 A/B 两个 agent 都上报 1000 个 READY,然后发布一个至少晚于当前时间 30 秒的 UTC `start_at`。两台机器按相同 `run_id` 和确定性调度执行。任何 agent 失联都将本轮标记为 `INVALID`。
|
||||
协调方式:Machine A 启动 coordinator,等待 A/B 两个 agent 都上报 1000 个 READY,然后发布一个至少晚于当前时间 30 秒的 UTC `start_at`。到达 `start_at` 后,A/B 必须在前后 3 秒内向 coordinator 完成最终起跑确认;只有双方都确认后才开始发送,任一方迟到则两边共同终止并将本轮标记为 `INVALID`。两台机器按相同 `run_id` 和确定性调度执行,任何 agent 失联同样判定为 `INVALID`。
|
||||
|
||||
`agent` 自身暴露 `/metrics`,提供连接状态、发布、接收、重复、乱序、断连及 Go/process 指标;ACK 和投递延迟分布写入最终 summary。`collect` 只执行只读查询,并把各数据源的原始快照与查询时间一起保存;连接凭证通过环境变量传入,不写入结果目录。
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# 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 容量问题。
|
||||
Reference in New Issue
Block a user