feat: stabilize multi-region runtime sync and worker orchestration
This commit is contained in:
417
docs/测试优化分析.md
Normal file
417
docs/测试优化分析.md
Normal file
@@ -0,0 +1,417 @@
|
||||
# 测试优化分析
|
||||
|
||||
> 说明:这份文档保留作历史分析记录。当前线上执行基线与观察方法,统一以 [当前线上最终Runbook.md](/www/wwwroot/getDomain/docs/当前线上最终Runbook.md) 为准。
|
||||
|
||||
更新时间:`2026-04-24 14:00`
|
||||
|
||||
## 这份文档是干什么的
|
||||
|
||||
这份文档不是讲理想情况,而是把这次已经做过的单机压测结果,和当前现场“根本没真正跑起来”的现状,放在一起说清楚。
|
||||
|
||||
目的很直接:
|
||||
|
||||
- 先把已经验证过的参数结论留档
|
||||
- 再把现在为什么看起来线程上限很高、实际只跑了很少线程说清楚
|
||||
- 给后面继续优化的人一个明确顺序,别再一边主链没跑通,一边继续盲目加并发
|
||||
|
||||
## 一句话结论
|
||||
|
||||
早上那轮“只测一台大陆机器”的压测,已经得出过一个单机最优值:
|
||||
|
||||
- `mainland-controller-01` 单机跑时,当前最优参数是 `35 进程 / 900 线程`
|
||||
|
||||
但这不等于项目现在已经能按这个参数稳定跑。
|
||||
|
||||
按 `2026-04-24 13:22` 左右控制面看到的实时状态,这套链路目前更像是:
|
||||
|
||||
- 任务还挂着
|
||||
- 页面也还能显示“运行中”
|
||||
- 但真正参与执行的只有很少几个桶
|
||||
- 总体上属于“没有真正跑起来”
|
||||
|
||||
所以现在的主矛盾不是“继续把线程调大”,而是“先让主流程真的跑起来”。
|
||||
|
||||
## 这次单机压测是怎么测的
|
||||
|
||||
这轮压测不是全链路压测,而是一个故意收缩后的测试环境。
|
||||
|
||||
当时做了这几件事:
|
||||
|
||||
- 先停掉 `121.204.244.248`,也就是 `mainland-worker-01`
|
||||
- 只保留 `mainland-controller-01` 单机承担检测
|
||||
- 停掉 controller 上的 `domaincheck-sync-agent.service`
|
||||
- 不再继续额外增加新的 Redis 限制动作
|
||||
- 直接在 controller 本地数据库里看 60 秒窗口真实完成量和更新量
|
||||
|
||||
这么做的目的,不是模拟线上真实规模,而是先排除多机互相干扰,看单机能跑到什么水平。
|
||||
|
||||
## 单机压测结果
|
||||
|
||||
压测时间段大致是 `2026-04-24 05:14 - 05:28`。
|
||||
|
||||
当时 controller 本地 60 秒窗口结果如下:
|
||||
|
||||
| 参数 | 60 秒完成量 | 60 秒更新量 | Redis 连接数 | 内存已用 | 可用内存 | 实际活跃 worker |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| `35/900` | `4092` | `22267` | `751` | `73.6G` | `54.3G` | `35` |
|
||||
| `40/1000` | `1885` | `1544` | `793` | `75.1G` | `52.9G` | `40` |
|
||||
| `45/1100` | `388` | `11347` | `818` | `75.7G` | `52.3G` | `45` |
|
||||
| `50/1200` | `0` | `12118` | `823` | `75.9G` | `52.1G` | `45` |
|
||||
|
||||
## 压测结论
|
||||
|
||||
从这轮结果里,可以直接得出 4 个结论:
|
||||
|
||||
### 1. 单机最优值不是越大越好
|
||||
|
||||
当前这套链路下,`35/900` 明显优于后面的更大参数。
|
||||
|
||||
也就是说,继续加进程、加线程,并没有带来更高完成量,反而更差。
|
||||
|
||||
### 2. 当前单机稳定上限大约在 `45/1100`
|
||||
|
||||
`45/1100` 还能勉强活住。
|
||||
|
||||
但它已经不是“跑得更快”,只是“还能挂着不死”。
|
||||
|
||||
### 3. `50/1200` 已经没有意义
|
||||
|
||||
虽然当时把参数抬到了 `50/1200`,但最终实际只活成了 `45` 个 worker,而且完成量已经掉到 `0`。
|
||||
|
||||
这说明不是配置没写进去,而是链路已经先撞到别的瓶颈了。
|
||||
|
||||
### 4. 当时真正的瓶颈已经不是 Redis
|
||||
|
||||
从这轮压测看,Redis 连接数有上升,但没有先爆。
|
||||
|
||||
更像是外部检测链路本身先撑不住了,尤其是:
|
||||
|
||||
- 注册检测
|
||||
- 代理池质量
|
||||
- 会话切换抖动
|
||||
|
||||
## 为什么当时测出来 `35/900`,现在页面却只剩 `52` 线程
|
||||
|
||||
这就是最容易误判的地方。
|
||||
|
||||
早上压测结论成立的前提是:
|
||||
|
||||
- 只测 controller 单机
|
||||
- `mainland-worker-01` 被停掉
|
||||
- `sync-agent` 被停掉
|
||||
- 现场是专门为压测收缩过的
|
||||
|
||||
而现在你看到的实时状态,已经不是那个压测场景了。
|
||||
|
||||
按 `2026-04-24 13:22` 左右接口返回的状态,现在的现场是:
|
||||
|
||||
- `detect/job/active` 里的活动任务还是 `job_id=1`
|
||||
- 这条任务从 `2026-04-21 23:30:42` 就已经是 `running`
|
||||
- `raw_items_pending=9478`
|
||||
- `raw_items_running=1`
|
||||
- `raw_items_completed=4996`
|
||||
- `raw_items_failed=486`
|
||||
|
||||
同时,`runtime/status` 给出的聚合状态是:
|
||||
|
||||
- `参与服务器:4`
|
||||
- `参与进程:4`
|
||||
- `参与线程:52`
|
||||
- `运行中:52`
|
||||
- `线程上限:52 / 2701`
|
||||
|
||||
这几组数字合起来说明的不是“系统已经高效跑起来”,而是:
|
||||
|
||||
- 页面还能看到一些参与桶
|
||||
- 但真正持续工作的执行面很薄
|
||||
- 大量任务还躺在队列里,没有被健康地持续消化
|
||||
|
||||
## 当前为什么可以直接判断“根本跑不起来”
|
||||
|
||||
不是因为页面数字小,而是因为几条关键事实同时出现了:
|
||||
|
||||
### 1. 活动任务太老
|
||||
|
||||
当前活动任务还是 `2026-04-21` 开出来的老任务。
|
||||
|
||||
如果主链真的健康,它不应该拖这么久还挂在 `running`。
|
||||
|
||||
### 2. 待处理量还很大
|
||||
|
||||
当前还剩:
|
||||
|
||||
- `9478` 条原始 pending
|
||||
- `3779` 条 claimed
|
||||
- `1028` 条 running backlog
|
||||
|
||||
但真正展示出来的活跃线程只有 `52`。
|
||||
|
||||
这个比例说明队列不是空了,而是消化不动。
|
||||
|
||||
### 3. 当前节点自己根本不跑本机 worker
|
||||
|
||||
当前 `overseas-control-01` 的 `runtime/status` 里明确写着:
|
||||
|
||||
- `worker_online=false`
|
||||
- `worker_process_count=0`
|
||||
- `worker_runtime_message=inactive/dead`
|
||||
|
||||
也就是说,现在这个页面看到的“运行中”,本来就不是本机在跑,而是靠远端汇总。
|
||||
|
||||
### 4. 远端参与桶很少,而且口径不稳
|
||||
|
||||
现在控制面聚合出的参与节点只有:
|
||||
|
||||
- `mainland-worker-01`
|
||||
- `mainland-controller-01-k`
|
||||
- `mainland-controller-01-a`
|
||||
- `mainland-controller-01-s`
|
||||
|
||||
这和早上单机压测时的 `35` 个 worker,根本不是一个级别。
|
||||
|
||||
所以你现在看到“线程上限 2701”,不要被这个数字迷惑。
|
||||
|
||||
它只是汇总上限,不代表这些线程真的都在跑。
|
||||
|
||||
## 现在最像什么问题
|
||||
|
||||
如果用人话说,现在最像下面这种情况:
|
||||
|
||||
- 任务队列里还有很多活
|
||||
- 但是执行面只剩很薄一层还在动
|
||||
- 页面还能看到“运行中”,所以看起来像没完全死
|
||||
- 可真正吞吐已经低到接近“没跑起来”
|
||||
|
||||
所以这不是单纯的参数问题,而是主流程状态已经歪了。
|
||||
|
||||
## 当前最该先做的事
|
||||
|
||||
下一步优化顺序建议固定成这样:
|
||||
|
||||
### 第 1 步:先恢复“能持续跑”
|
||||
|
||||
先不要继续加进程、加线程。
|
||||
|
||||
先确认 4 件事:
|
||||
|
||||
- 当前到底是哪几个节点真的在跑
|
||||
- `mainland-worker-01` 是不是还应该继续停着
|
||||
- controller 上当前实际活着多少 worker
|
||||
- 老任务里这些 `claimed/running` 有没有大量卡死项
|
||||
|
||||
### 第 2 步:把老任务和会话问题理顺
|
||||
|
||||
现在这条 `2026-04-21` 的老任务已经拖太久了。
|
||||
|
||||
要优先确认:
|
||||
|
||||
- 是不是有很多旧 `session_replaced`
|
||||
- 是不是有大量任务项一直卡在 `claimed/running`
|
||||
- 是不是页面显示还在跑,但实际 worker 已经不工作了
|
||||
|
||||
### 第 3 步:确认代理链是不是主瓶颈
|
||||
|
||||
早上单机压测已经说明,Redis 不是先撞墙的那个点。
|
||||
|
||||
下一步更值得盯的是:
|
||||
|
||||
- `detect_register` 的真实完成率
|
||||
- 代理池是否经常空
|
||||
- RDAP 访问是否大量超时
|
||||
|
||||
### 第 4 步:只有主链恢复后,才重新做性能优化
|
||||
|
||||
## 13:48 补充结论
|
||||
|
||||
这轮又确认了一件非常关键的事:
|
||||
|
||||
- `mainland-worker-01` 不是“已经彻底消失”,而是“被标记为停用,但之前还在继续上报运行态”
|
||||
|
||||
后来实际做了两步处理:
|
||||
|
||||
- 先通过远端 `ops job` 让 `mainland-worker-01` 本机执行 `systemctl stop domaincheck-worker`
|
||||
- 再把控制面的状态汇总改成:只要节点在 `ops_managed_nodes` 里是 `is_enabled=false`,就不再算进在线 Worker、参与节点和活跃线程
|
||||
|
||||
处理完成后,控制面实时口径已经从“2 台参与节点 / 2 个检测进程 / 2 线程”收到了:
|
||||
|
||||
- `参与节点:1`
|
||||
- `参与进程:1`
|
||||
- `运行中:1`
|
||||
- 只剩 `mainland-controller-01`
|
||||
|
||||
这说明前面那种“明明已经准备单机测了,页面还一直把停用节点算进去”的问题,确实是状态口径 bug,不是现场真的还有两台健康执行机一起在跑。
|
||||
|
||||
所以从现在开始再看单机测试结果时,要以这条新口径为准:
|
||||
|
||||
- `mainland-worker-01` 已停用,不再参与单机压测统计
|
||||
- 当前有效执行面只剩 `mainland-controller-01`
|
||||
|
||||
等主流程恢复成“真的能持续吃队列”之后,再拿 `35/900` 作为第一版基线。
|
||||
|
||||
到那时可以按下面顺序再调:
|
||||
|
||||
1. 先验证 `35/900` 在恢复后的现场还能不能稳定
|
||||
2. 如果稳定,再测 `40/1000`
|
||||
3. 只有吞吐确实提高了,才继续往上加
|
||||
|
||||
## 这份文档的最终结论
|
||||
|
||||
可以把这次结果浓缩成两句话:
|
||||
|
||||
第一句:
|
||||
|
||||
- 单机压测已经证明,当前代码和链路下,`mainland-controller-01` 的最佳参数是 `35 进程 / 900 线程`
|
||||
|
||||
第二句:
|
||||
|
||||
- 但当前线上真正的问题不是“参数不够大”,而是“主流程根本没有持续跑起来”,所以现在继续加线程没有意义
|
||||
|
||||
后面再优化时,应该先解决“为什么只剩 52 线程在动”,再谈如何把吞吐重新抬高。
|
||||
|
||||
## 2026-04-24 13:35 补充结论
|
||||
|
||||
这份文档写完以后,又继续往下排了一轮,现场有两个非常关键的新结论。
|
||||
|
||||
### 1. `52` 线程里有明显口径错配
|
||||
|
||||
后来继续核对后发现,页面里那组:
|
||||
|
||||
- `参与服务器:4`
|
||||
- `参与进程:4`
|
||||
- `参与线程:52`
|
||||
|
||||
并不是来自 `detect_job_items` 真表本身。
|
||||
|
||||
真实数据库里,`job_id=1` 其实只剩:
|
||||
|
||||
- `1` 条 `running`
|
||||
|
||||
而且这条记录最后更新时间还停在 `2026-04-22 15:00:56`,本质上已经是老僵死项。
|
||||
|
||||
真正把页面抬到 `52` 的,是一条口径 bug:
|
||||
|
||||
- 当前 active job 还是老任务 `detect-20260421232649-acfa3d`
|
||||
- 但控制面又拿到了别的 sync job 的 runtime overlay
|
||||
- 两套数据被硬合到了一起
|
||||
|
||||
简单说就是:
|
||||
|
||||
- 任务表是老 job
|
||||
- 线程数却借用了别的 job 的运行态
|
||||
|
||||
### 2. 这条口径 bug 已经修掉了
|
||||
|
||||
现在代码已经补成:
|
||||
|
||||
- 只有 runtime overlay 的 `job_code / job_id` 和当前 active job 对得上时,才允许覆盖 `queue_health`
|
||||
- 对不上时,宁可退回真实任务表,也不再把别的 job 的运行态硬套到当前页面上
|
||||
|
||||
修完并重启海外控制面 API 之后,实时口径已经从之前的 `52` 收回到了:
|
||||
|
||||
- `参与节点:2`
|
||||
- `参与进程:2`
|
||||
- `参与线程:2`
|
||||
|
||||
当前这 `2` 个真实信号分别来自:
|
||||
|
||||
- `mainland-controller-01` 那条老 `running`
|
||||
- `mainland-worker-01` 当前 heartbeat 上报的 `1` 条活跃线程
|
||||
|
||||
这说明一件事:
|
||||
|
||||
- 之前那组 `52` 的确主要是错口径,不是实际吞吐
|
||||
|
||||
## 目前还剩的真实问题
|
||||
|
||||
虽然 `52 -> 2` 这一步已经收准了,但项目还是没有真正恢复健康。
|
||||
|
||||
当前还剩 2 个真实问题:
|
||||
|
||||
### 1. `mainland-worker-01` 明明已停用,却还在持续推运行态
|
||||
|
||||
当前控制面日志里还能持续看到:
|
||||
|
||||
- `121.204.244.248` 往海外控制面打 `runtime/debug-ingest`
|
||||
- 同时也还在打 `ops/agent/pull`
|
||||
|
||||
这说明这台机器不是“旧残影”,而是还真的在线、还真的在报活。
|
||||
|
||||
所以“已停用”这件事,目前只是在托管配置层停了,但没有真正把它从 Agent / runtime 上报链里摘干净。
|
||||
|
||||
### 2. `job_id=1` 这条老任务本身也还没收口
|
||||
|
||||
当前老任务依然挂着:
|
||||
|
||||
- `job_id=1`
|
||||
- `job_code=detect-20260421232649-acfa3d`
|
||||
- `started_at=2026-04-21 23:30:42`
|
||||
|
||||
而真实任务表里,它现在已经不是“很多线程在跑”,而是:
|
||||
|
||||
- 大部分还在 `pending`
|
||||
- 只剩 1 条老 `running`
|
||||
|
||||
这说明这条老任务本身也需要后续专门处理,不能继续长期挂在 `running`。
|
||||
|
||||
## 现在更准确的下一步
|
||||
|
||||
到这里为止,下一步就更明确了:
|
||||
|
||||
1. 先把 `mainland-worker-01` 真的摘掉。
|
||||
2. 再处理 `job_id=1` 这条老任务的遗留 `running` 项。
|
||||
3. 等这两件事收完,再重新看当前真实执行面到底还有没有持续吞吐。
|
||||
|
||||
也就是说,现在已经不是“继续猜线程参数”的阶段,而是“先把错误执行面和老任务残留清掉”的阶段。
|
||||
|
||||
## 新增一版“单机性能模式”给注册检测
|
||||
|
||||
这次为了后面继续压单机吞吐,我已经在代码里补了一版只影响“注册状态检测”的性能模式。
|
||||
|
||||
它的目标不是改全局架构,而是先把当前最像瓶颈的那一段链缩短:
|
||||
|
||||
- 只改 `detect_register`
|
||||
- 不碰百度、360、站长、爱站、时光机这些步骤
|
||||
- 保留当前版的连接复用和失败闭环
|
||||
- 只把注册检测改得更像老版本那种“先尽快打出去,再说”
|
||||
|
||||
### 这版模式做了什么
|
||||
|
||||
打开后,注册检测会变成下面这个思路:
|
||||
|
||||
- 即使全局 `allow_direct=false`,注册检测这一步也允许直连
|
||||
- 默认先连续直连 `2` 次
|
||||
- 这两次之间不再等代理补货
|
||||
- 只有前面的直连没打通,后面才继续走代理兜底
|
||||
|
||||
简单说就是:
|
||||
|
||||
- 当前默认模式:代理优先,直连兜底
|
||||
- 单机性能模式:注册检测直连优先,代理兜底
|
||||
|
||||
### 怎么开
|
||||
|
||||
这版先做成环境变量开关:
|
||||
|
||||
- `DOMAINCHECK_REGISTER_SINGLE_MACHINE_MODE=1`
|
||||
- `DOMAINCHECK_REGISTER_DIRECT_STREAK_ATTEMPTS=2`
|
||||
|
||||
当前默认建议先用这组:
|
||||
|
||||
- `进程:35`
|
||||
- `线程:900`
|
||||
- `DOMAINCHECK_REGISTER_SINGLE_MACHINE_MODE=1`
|
||||
- `DOMAINCHECK_REGISTER_DIRECT_STREAK_ATTEMPTS=2`
|
||||
|
||||
如果后面继续试更激进一点,再考虑把直连连打次数从 `2` 提到 `3`,但不建议一上来就抬。
|
||||
|
||||
### 为什么不是直接回退老版本
|
||||
|
||||
老版本真正有参考价值的是“链短、切得快”,不是它的实现本身更先进。
|
||||
|
||||
所以这次没有回退这几样东西:
|
||||
|
||||
- 没回退到 `20s * 3` 的长超时
|
||||
- 没回退到“注册失败也继续往后跑”
|
||||
- 没丢掉当前的 `Session` 复用
|
||||
|
||||
换句话说,这次是“借老策略”,不是“退老实现”。
|
||||
Reference in New Issue
Block a user