75 lines
5.6 KiB
Markdown
75 lines
5.6 KiB
Markdown
# ProfileMax(CloakB + ipmart.io-US)并发常驻浏览器压测报告
|
|
|
|
- 日期:2026-10-09
|
|
- 服务器:OVH-FR `37.187.92.169`(8 核 / 31GB 内存 / /dev/shm 16G / 同机还跑 mysql、clickhouse、plausible 等其它服务)
|
|
- 链路:ProfileMax admin(127.0.0.1:5310)→ 节点 OVH-FR(node :3106,systemd 直跑,无 Docker)
|
|
- 测试身份:MCP 用户 phptits(id=2);Profile 全部归属该用户
|
|
- 方式:节点 API `POST /profiles/:id/lease(ttlMs=2h)` → `POST /profiles/:id/browser/start` → CDP `/json/new` + WS `Page.navigate` 开真实页面;每 20s 扫 `/proc` 按 `--user-data-dir` 聚合每 Profile 进程树
|
|
- 内存口径:**PSS**(smaps_rollup,去共享重复);RSS 会把共享页重复计数(报告注明)
|
|
|
|
## 配置
|
|
|
|
| 项 | 值 |
|
|
|---|---|
|
|
| 浏览器内核 | CloakB(cloak,146.0.7680.177.5) |
|
|
| 代理 | `ipmart.io-US`(socks5 gateway.ipmart.io:9999,用户名含 `{sid}` → 每指纹独立 US 住宅 sticky 出口) |
|
|
| 标签 | `SERP` |
|
|
| 模式 | 无头(用户中途指示全部改无头) |
|
|
| 保活 | `BROWSER_IDLE_TIMEOUT=0`(禁用 10 分钟空闲自动关闭)+ 每 Profile 2 小时 lease |
|
|
| Profile 池 | 10,002 个(`sz-serp-*`,CloakB + ipmart.io-US + SERP) |
|
|
|
|
## 结果总览
|
|
|
|
| 指标 | 测试1:3 标签(google/yahoo/x) | 测试2:1 标签(google) |
|
|
|---|---|---|
|
|
| **同时常驻浏览器上限** | **82 个** | **189 个** |
|
|
| 常驻时长 | ≥40 分钟(最老的) | ≥40 分钟(最老的) |
|
|
| 系统峰值 PSS | 25.0GB | 25.5GB |
|
|
| 内存最低可用(avail) | **542MB(耗尽)** | 2793MB |
|
|
| swap 峰值 | **9.2GB 全满(耗尽)** | 7.6GB(触发停止线) |
|
|
| load 峰值(8 核) | **118(kswapd 抢转,CPU 也耗尽)** | 46 |
|
|
| 总进程/线程数峰值 | 1280 进程 | 1702 进程 |
|
|
| 每浏览器进程数(均值) | 15.1(重载页到 50+) | 9.0 |
|
|
| 每 Profile 内存(稳定期) | p50=125 / p90=906 / max=1560 MB;总量均值 ≈305MB | p50=152 / p90=158 / max=179 MB;总量均值 ≈129MB |
|
|
| 每 Profile swap | p50=20MB | p50=2MB |
|
|
| 每 Profile CPU(约 40 分钟常驻) | 均值 ≈164s | 均值 ≈46s |
|
|
| 每 Profile 磁盘(user-data) | p50=25MB / max=115MB | p50=10MB / max=115MB |
|
|
|
|
**结论:当前服务器(31GB 内存,其余 3.7GB 被 mysql/clickhouse/系统占用)**
|
|
- 3 标签(轻量页)可常驻 **约 80 个** Profile;3 标签里出现 yahoo/x 重页时重载 Profile 会拖到 1.5GB/个,是方差的主因。
|
|
- 1 标签(google)可常驻 **约 190 个** Profile(停止在 swap 7.2GB 保守线,真实极限约 200±10)。
|
|
- 内存是第一瓶颈,PSS 是衡量口径;3 标签场景 CPU(kswapd + 软渲染)会成为第二瓶颈。
|
|
|
|
## 每轮细节
|
|
|
|
### 测试1(3 标签)
|
|
- 发射阶段:并发 4,fd 修复后从 58 → 82 一路推进;`MemAvailable<1.5GB` + swap 满后页面加载/启动开始超时与抖动(load 118),停止发射。
|
|
- 残留 82 个全部驻留至数据采集完成(带 OOM 保护策略:浏览器主进程 oom_score_adj=200,其它服务进程=-600,整个压测期间 mysqld/clickhouse/PG 等零损伤)。
|
|
- 重载 Profile(x.com/yahoo 富页)实测会把进程树拉到 50+ 进程 / 1.5GB PSS。
|
|
|
|
### 测试2(1 标签)
|
|
- 清场回收后重跑同一 10002 池;发射 25 分钟到 189 个,唯一失败 1 次;swap 7458MB ≥ 7200MB 触发保守停止(avail 仍有 3.3GB,真实极限略高)。
|
|
- 单标签 google 的内存分布极稳(129MB 均值,极差 179)——google 轻页没有长尾。
|
|
|
|
## 过程中修复的 Docker/系统/产品问题
|
|
|
|
1. **`fs.file-max=65535` 系统级文件句柄上限**——58 个浏览器即吃满(65216/65535),新 chrome 启动静默失败 → 启动超时。已放宽:`fs.file-max=2097152`(已写入 /etc/sysctl.conf)。
|
|
2. **inotify 额度**:max_user_instances 128→1024、max_user_watches 261756→524288(chrome 每 Profile 多个 watcher,128 实例上限也会卡死启动)。
|
|
3. **swap 仅 1GB**:追加 8G swapfile `/testswap`(priority 10),现共 9GB,保留作为长期优化(防 OOM 误伤同机其它服务)。
|
|
4. **10 分钟空闲自动关闭**(节点 `BROWSER_IDLE_TIMEOUT` 默认 600s):测试期间置 0 禁用,已恢复默认。
|
|
5. 有头模式(浏览器经 VNC/WebDesk 跑在每 Profile Xvfb 上,llvmpipe 软渲染)在批量场景会瞬间把 8 核打满(load 96),**批量常驻必须用无头**;webdesk 二进制与 x11vnc 测试期间暂移、已恢复。
|
|
6. 产品坑(记录进记忆):节点有「UI 误关自动复活」(SIGKILL 杀浏览器会被复活,重启风暴);停浏览器必须走 API 显式 stop(closing 路径)。`browser/start` 返回的 `processId` 是包装进程(拉起 chrome 后退出),不能当存活判据;存活判据应按 `--user-data-dir` 进程树聚合。响应体统一 `{success,data}` 包装。
|
|
7. admin `DELETE /api/profiles/:id` 只删库行、**不清理节点侧 Profile 数据**(节点清理只在转移链路触发)——压测清理需要另对节点调 `DELETE /profiles/:id?force=true`(本次已处理)。
|
|
|
|
## 清理与恢复
|
|
|
|
- 测试 Profile:admin 侧 10,002 个全部删除;节点侧已启动过的 190 个数据目录全部强删(`force=true`);节点现存 139 个目录均为合法 Profile(未动)。
|
|
- 配置:webdesk 二进制、x11vnc、`BROWSER_IDLE_TIMEOUT`、全部恢复;`fs.file-max`/inotify/8G swapfile 保留(系统优化项)。
|
|
- 终态:节点在线(0 running)、admin 正常、内存回收(avail 29GB)、swap 余量 6.2GB。
|
|
|
|
## 复现物
|
|
|
|
- 服务器 `/root/serpmax-stress/`:`create-profiles.ts / run-stress.ts / sampler.ts / stop-browsers.ts / report.ts / cleanup-profiles.ts / node-wipe-stress.ts`
|
|
- 数据:`data/test{1,2}-summary.json|md`、`data/test1-stats-final.jsonl`(154 轮)、`data/test2-stats.jsonl`(117 轮)、各轮 run 日志
|
|
- 本地:`/home/bigcode/data/dev/bun/SerpMax/stress/REPORT.md`(本文件)+ 同套脚本与 summary JSON
|