Files
getDomain/docs/测试优化分析.md

418 lines
14 KiB
Markdown
Raw Permalink 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.
# 测试优化分析
> 说明:这份文档保留作历史分析记录。当前线上执行基线与观察方法,统一以 [当前线上最终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` 复用
换句话说,这次是“借老策略”,不是“退老实现”。