From 5063100a90282ad21bda0912a575d30f1bf0d153 Mon Sep 17 00:00:00 2001 From: bigcode Date: Wed, 4 Nov 2026 06:31:29 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20xLightpanda=20enablejs=20=E8=BD=AF?= =?UTF-8?q?=E9=97=A8=E7=AA=81=E7=A0=B4(0.10.5=20Accept-CH=20=E5=9B=9E?= =?UTF-8?q?=E5=B8=A6)=E2=80=94=E2=80=94=20=E7=81=B0=E5=BA=A6=E7=8E=B0?= =?UTF-8?q?=E7=8A=B6/=E6=A0=B9=E5=9B=A0/=E7=81=B0=E5=BA=A6=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=89=8B=E5=86=8C=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/02_STORAGE_HOOK.md | 526 +++++++++++++++++++++++++++++++++ docs/03_XLIGHTPANDA_ROLLOUT.md | 23 +- 2 files changed, 539 insertions(+), 10 deletions(-) create mode 100644 docs/02_STORAGE_HOOK.md diff --git a/docs/02_STORAGE_HOOK.md b/docs/02_STORAGE_HOOK.md new file mode 100644 index 0000000..348b626 --- /dev/null +++ b/docs/02_STORAGE_HOOK.md @@ -0,0 +1,526 @@ +# ProfileMax Storage Hook / StorageFS 开发设计 + +> 项目:ProfileMax +> 模块:ProfileMax Node Storage Hook +> 目标:为 ProfileMax Node 上所有 Profile 数据提供统一存储入口,统一管理持久化磁盘与 tmpfs 临时存储。 +> 原则:IO 优先、低 CPU、不压缩、不依赖节点数据库;所有 Profile 数据访问均经过 Storage Hook。 + +## 1. 设计目标 + +Storage Hook 是 ProfileMax Node 上**所有 Profile 存储的统一入口**,不只是 Chromium Profile 主目录的磁盘分配器。Chromium、impersonate-proxy 以及 Profile 相关服务产生的文件,都必须通过 Storage Hook 管理。 + +Storage Hook 统一提供两类存储: + +1. **持久化存储**:多磁盘 StorageFS,保存需要跨进程重启、Profile 重启继续存在的数据。 +2. **临时存储**:由 Storage Hook 创建和管理的 tmpfs,承载可丢弃、可重新生成或已由代理层缓存的重复数据。 + +核心目标: + +1. **IO 优先**:读写路径短,减少复制、锁竞争、系统调用和不必要的元数据操作。 +2. **低 CPU**:不做压缩/解压,不做内容哈希,不在热路径执行复杂算法。 +3. **避免重复缓存**:impersonate-proxy 已缓存的 HTTP 资源,不再让 Chromium 将同一份 HTTP Cache 持久化到磁盘。 +4. **减少底层 inode**:按收益选择 Pack,不强制所有文件打包。 +5. **统一入口**:所有 Profile 数据,包括 impersonate-proxy 的缓存目录,都由 Storage Hook 分配、挂载和监控。 +6. **节点无数据库**:Storage Hook 不连接 PostgreSQL、SQLite、Redis 或 Admin 数据库。 +7. **数据语义正确**:对 SQLite、LevelDB 等活跃数据库文件提供正确的锁、同步、重命名和随机读写语义。 + +不以压缩率、去重率或极限节省磁盘空间作为目标。 + +## 2. 总体架构 + +```text + ProfileMax Admin + (唯一配置数据库持有方) + | + Storage Hook 配置下发 + | + v ++--------------------------------------------------------------+ +| ProfileMax Node | +| | +| Chromium Profile impersonate-proxy Profile Services | +| | | | | +| +-----------------------+--------------------+ | +| | | +| v | +| Storage Hook (统一入口) | +| | | +| +-------------------+-------------------+ | +| | | | +| v v | +| Persistent Storage tmpfs Manager | +| StorageFS / FUSE Linux tmpfs | +| 多磁盘 / Direct / Pack 临时缓存路径 | +| | | | +| v v | +| /mnt/storage-* /run/profilemax/* | +| 持久化 Profile 数据 可丢弃缓存数据 | ++--------------------------------------------------------------+ +``` + +职责边界: + +- Admin 数据库只保存每个 Node 的 Storage Hook 配置,不保存文件、目录、inode、Pack 或 Profile 文件映射。 +- Node 接收配置后,在本地完成持久化存储、tmpfs 创建、路径挂载、容量控制和恢复。 +- Chromium 与 impersonate-proxy 都使用 Storage Hook 提供的路径,不允许各自绕过 Hook 写入未管理目录。 +- Storage Hook 不连接 Admin 数据库,也不运行 SQLite 等数据库。 +- Node 持久化元数据使用 StorageFS 自己的二进制索引、日志和快照文件;tmpfs 内容不进入持久化元数据恢复流程。 + +Admin 与 Node 的职责严格分离: + +- Admin 数据库只保存节点 Storage Hook 的配置,不保存文件、目录、inode、Pack 或 Profile 文件映射。 +- Node 接收配置后在本地执行挂载、分配、读写和恢复。 +- Node Storage Hook 不连接 Admin 数据库,也不运行 SQLite 等数据库。 +- Node 的本地元数据使用 StorageFS 自己的二进制索引、日志和快照文件,不将其实现成数据库服务。 + +## 3. Admin 配置模型 + +Admin 数据库只需要保存每个 Node 的 Storage Hook 配置。配置内容包括: + +- 是否启用 Storage Hook。 +- 统一存储根目录和元数据目录。 +- 多个持久化 Volume 的标识、物理目录、容量上限、分配权重。 +- tmpfs 总容量、单 Profile 上限、挂载路径规则及目录路由规则。 +- Chromium 缓存目录策略与 impersonate-proxy 缓存目录策略。 +- Pack 大小和小文件阈值(如果启用 Pack)。 +- IO、刷盘和预留空间等运行参数。 +- 配置版本号,用于 Node 热更新和回滚。 + +示例配置: + +```json +{ + "enabled": true, + "mount_point": "/var/lib/profilemax/storage", + "metadata_path": "/var/lib/profilemax/storage-meta", + "volumes": [ + { + "id": "volume-a", + "path": "/mnt/storage-a/profilemax", + "capacity_gib": 500, + "weight": 1 + }, + { + "id": "volume-b", + "path": "/mnt/storage-b/profilemax", + "capacity_gib": 1000, + "weight": 2 + } + ], + "tmpfs": { + "enabled": true, + "root": "/run/profilemax", + "total_size_gib": 16, + "per_profile_limit_mib": 512, + "mode": "per-profile" + }, + "routes": { + "chromium_http_cache": "tmpfs", + "chromium_rebuildable_cache": "tmpfs", + "impersonate_proxy_cache": "persistent" + }, + "io": { + "pack_enabled": true, + "small_file_threshold": 65536, + "pack_segment_size_mib": 256, + "read_cache_mib": 256, + "write_batch_max_ms": 2, + "sync_policy": "data" + } +} +``` + +以上为配置结构示例。容量单位统一使用 GiB;持久化 Volume 容量上限是 StorageFS 逻辑分配上限;tmpfs 容量是内存/交换空间预算,必须单独限制,不能与磁盘容量混为一谈。路由规则必须支持按 Profile 相对路径匹配,并由 Node 在启动 Profile 前完成挂载。 + +以上为配置结构示例。容量单位统一使用 GiB;容量上限是 StorageFS 的逻辑分配上限,不等同于底层文件系统的硬配额。 + +配置下发流程: + +1. Admin 保存配置并递增 config_version。 +2. Node 收到配置后校验路径、权限、磁盘可用空间和配置版本。 +3. Node 创建缺失目录并初始化 Volume。 +4. Storage Hook 应用配置并挂载 StorageFS。 +5. Node 上报实际挂载状态、Volume 可用容量和错误信息。 +6. 配置变更不得直接删除已有数据;Volume 下线必须先进入迁移/只读流程。 + +Node 可以在本地保存一份配置快照用于重启恢复,但该文件只是配置缓存,不是数据库,也不作为配置权威来源。 + +## 4. Node 本地目录布局 + +建议: + +```text +/var/lib/profilemax/ +├── storage/ # StorageFS 统一持久化挂载点 +└── storage-meta/ # StorageFS 内部元数据 + +/run/profilemax/ +└── profiles/ + └── / # 每个 Profile 的 tmpfs 挂载目录 + +/mnt/storage-a/profilemax/ +├── packs/ +└── direct/ + +/mnt/storage-b/profilemax/ +├── packs/ +└── direct/ +``` + +要求: + +- metadata 与数据 Volume 分离,避免 Pack 写入挤占元数据日志空间。 +- Pack 使用预分配或按需扩展的固定上限 Segment。 +- Segment 不压缩,文件内容按原始字节存储。 +- 不对每个 Profile 创建一套独立的内部数据库。 +- tmpfs 由 Storage Hook 统一创建、限额、挂载和清理,不由 Chromium 或代理进程自行创建。 +- tmpfs 不得覆盖整个 Profile 根目录,只挂载到明确允许丢弃的子目录。 +- Profile 退出时按策略销毁该 Profile 的 tmpfs;重新启动时重新创建空缓存目录。 + +## 4.1 Profile 路径路由与 tmpfs 策略 + +Storage Hook 必须维护一份明确的路径路由规则。Profile 根目录和所有子目录仍属于 Storage Hook 的统一命名空间,但不同子目录可以映射到不同后端。 + +**A. 浏览器 HTTP Cache:tmpfs** + +impersonate-proxy 已经缓存 HTTP 响应资源时,Chromium 本地 HTTP Cache 属于重复缓存。浏览器侧对应目录应挂载到 tmpfs,避免重复写入持久化磁盘: + +- `Default/Cache/` +- Chromium 实际版本使用的 HTTP Cache 数据目录及其索引文件。 + +该策略只针对浏览器 HTTP 网络缓存,不代表所有网站存储都可以丢弃。 + +**B. 可重建缓存:tmpfs** + +以下目录通常可重新生成,默认可路由到 tmpfs: + +- `Default/Code Cache/` +- `GPUCache/` +- `GrShaderCache/` +- `ShaderCache/` + +实际路径应根据 Chromium 版本和启动参数检测,不得依赖单一固定目录名。 + +**C. 必须持久化的数据** + +以下数据默认留在 StorageFS 持久化后端: + +- Cookies、Local Storage、IndexedDB。 +- Preferences、登录态及 Profile 配置。 +- Extension 数据及其状态。 +- Service Worker 注册信息和 CacheStorage(除非业务明确允许清空)。 +- SQLite/LevelDB 数据目录及其 WAL/SHM 文件。 +- 其他无法确认可重建的 Profile 数据。 + +禁止将整个 `Default/`、整个 `Service Worker/` 或整个 Profile 目录直接挂载到 tmpfs。 + +**D. impersonate-proxy 缓存** + +impersonate-proxy 的缓存目录也必须位于 Storage Hook 命名空间内,不能绕过 Hook 直接写入任意节点目录。默认将代理缓存放在持久化 StorageFS 中,以便跨 Profile/浏览器重启复用;如果代理缓存被明确配置为短期缓存,也允许由 Admin 将该目录路由到 tmpfs。 + +浏览器侧 HTTP Cache 与代理侧缓存是两份不同的缓存:默认保留代理侧缓存,浏览器侧重复 HTTP Cache 使用 tmpfs。两者不得误配置成同一个目录或互相覆盖。 + +**E. 挂载生命周期** + +1. Node 启动时先挂载 StorageFS。 +2. 创建 Profile 时,Storage Hook 创建该 Profile 的 tmpfs 目录并应用路径路由。 +3. Chromium 与 impersonate-proxy 启动前,检查所有必需挂载均已就绪。 +4. Profile 运行期间,Storage Hook 统计 tmpfs 使用量并执行硬容量限制。 +5. Profile 正常停止后销毁 tmpfs;异常退出后由 Node 清理残留挂载。 +6. tmpfs 满时只允许缓存写入失败或按明确策略清理可重建缓存,不得影响 Cookies、IndexedDB 等持久化数据。 + +## 5. 文件存储策略 + +StorageFS 使用 Hybrid Backend,不强制所有文件都进入 Pack。 + +### 5.1 Pack Backend:小文件 + +小文件可合并写入 Pack Segment,以减少底层 inode 数量。 + +Pack 记录包含: + +- object_id +- segment_id +- offset +- length +- generation +- flags + +元数据索引负责把虚拟文件映射到 Pack 的 offset/length。 + +写入流程: + +1. 从内存索引查询或创建文件元数据。 +2. 在当前 Segment 分配连续空间。 +3. 直接写入原始数据,不压缩、不加密、不计算内容哈希。 +4. 追加必要的元数据日志。 +5. 更新内存索引。 + +读取流程: + +1. 从内存索引获取 segment、offset 和 length。 +2. 使用 pread/preadv 读取目标范围。 +3. 尽可能使用内核 Page Cache。 +4. 不经过解压、反序列化或额外的数据转换。 + +Pack 适合大量小型、非频繁随机修改的文件。Pack 记录更新采用新位置写入和旧记录回收,不在原位置进行复杂的碎片整理。 + +### 5.2 Direct Backend:活跃数据库和大文件 + +以下文件/目录优先使用 Direct Backend: + +- SQLite 数据库及其 WAL/SHM 文件。 +- LevelDB/RocksDB 等依赖文件锁和随机写入语义的目录。 +- 大文件。 +- 高频随机更新文件。 +- 需要 mmap、fallocate 或严格 POSIX 语义的文件。 + +Direct Backend 由底层文件系统直接承载,StorageFS 只管理命名空间和路径映射,尽可能通过 FUSE passthrough 或等效机制减少数据面拷贝。 + +**不得把 SQLite、LevelDB 的活跃文件打包后再用简化接口模拟普通文件。** 如果无法完整满足锁、mmap、fsync、rename 等语义,必须使用 Direct Backend。 + +### 5.3 默认阈值 + +初始建议: + +- 小于 64 KiB:允许进入 Pack。 +- 大于或等于 64 KiB:Direct Backend。 +- 数据库文件及其关联目录:强制 Direct Backend。 +- 阈值必须可由 Admin 配置,后续通过实测调整。 + +Pack 不意味着所有小文件都适合打包。高频修改、频繁 truncate 的小文件也应允许强制 Direct。 + +## 6. IO 与 CPU 优化要求 + +性能目标是减少热路径开销,而不是增加复杂的存储算法。 + +### 6.1 禁止进入热路径的操作 + +- 不压缩、不解压。 +- 不做内容哈希或跨 Profile 去重。 +- 不进行每次读写的 JSON 序列化。 +- 不在每次文件访问时访问 Admin。 +- 不在每次 IO 时扫描目录或扫描 Pack。 +- 不在读路径执行 GC、全量统计或容量重平衡。 +- 不为每个文件启动独立任务或线程。 + +### 6.2 数据面优化 + +- 使用 Rust 实现,尽量减少不必要的内存分配和数据复制。 +- 使用 FUSE low-level API,避免高层封装带来的额外开销。 +- 优先评估 Linux FUSE passthrough,让 Direct Backend 的数据读写尽可能绕过用户态数据复制。 +- 对 Pack 使用 pread/pwrite、preadv/pwritev 等位置 IO,避免共享文件游标锁。 +- 使用有界 IO 队列和批量提交,避免无限队列导致内存增长。 +- 元数据索引常驻内存,启动时通过快照和日志恢复,正常访问不查磁盘索引。 +- 利用 Linux Page Cache;不要默认使用 O_DIRECT,避免绕过有效缓存。 +- Segment 可预分配,降低写入时的文件扩展和碎片开销。 +- 对同一 Segment 的写入采用轻量分配器,避免全局大锁。 + +### 6.3 元数据优化 + +- 使用紧凑二进制结构,不使用 JSON 存储运行时元数据。 +- 目录项和 inode 元数据采用分层索引。 +- inode/object ID 单调分配,避免频繁随机生成。 +- 目录项查询优先走内存索引。 +- 元数据日志采用 append-only 方式,减少随机更新。 +- 周期性生成快照;恢复时加载快照并重放增量日志。 +- 元数据日志与数据 Pack 分开存放。 + +## 7. 元数据与恢复机制 + +StorageFS 不使用数据库。 + +本地元数据由以下文件构成: + +```text +storage-meta/ +├── superblock +├── metadata.snapshot +├── metadata.wal +└── volume.state +``` + +- superblock:文件系统版本、UUID、配置版本和基础参数。 +- metadata.snapshot:inode、目录项和对象映射的紧凑快照。 +- metadata.wal:快照之后的增量元数据操作。 +- volume.state:各 Volume、Segment 的分配与恢复状态。 + +要求: + +1. WAL 记录具备长度和版本字段,能够识别尾部不完整记录。 +2. 启动时先加载快照,再顺序重放 WAL。 +3. 崩溃恢复不得依赖 Admin 在线。 +4. WAL 达到阈值后生成新快照,再安全截断旧日志。 +5. 数据写入与元数据提交必须定义明确的顺序,避免索引指向尚未落盘的数据。 +6. 通过 fsync/fdatasync 提供明确的持久化语义,不得用异步写入冒充同步完成。 + +## 8. 多磁盘分配 + +Allocator 负责选择 Volume 和 Segment,不参与文件数据读写。 + +分配原则: + +1. 只向已启用、可写且未达到容量上限的 Volume 分配。 +2. 根据剩余容量、配置权重和当前 IO 压力选择 Volume。 +3. 新文件/新 Pack 优先分配,不在每次读写时重新平衡。 +4. 已存在的对象不因权重变化而自动搬迁。 +5. 磁盘达到水位时停止新分配并上报 Admin。 +6. Volume 故障时不得静默丢弃元数据或把原数据映射到其他 Volume。 + +初始实现使用容量权重分配即可,不引入复杂的实时全局调度算法。IO 压力指标可先用于监控,后续有实测收益再参与分配决策。 + +## 9. Profile 命名空间 + +Profile 在 StorageFS 中表现为普通目录: + +```text +/var/lib/profilemax/storage/ +├── profiles/ +│ ├── / +│ ├── / +│ └── / +└── shared/ +``` + +- Profile 生命周期由 ProfileMax Node 管理。 +- Storage Hook 不保存 Profile 业务数据,不建立 profiles 数据库表。 +- Profile 目录的 inode、目录项和对象映射属于 StorageFS 内部元数据。 +- Profile 删除由 Node 调用 StorageFS 接口完成,删除过程必须支持可恢复状态,避免中途崩溃产生悬挂对象。 +- 是否提供 Profile 级容量限制作为后续能力;第一阶段不增加不必要的配额索引和统计热路径。 + +## 10. POSIX 兼容要求 + +StorageFS 面向 Chromium Profile,必须重点验证: + +- open / close / read / write / pread / pwrite +- mkdir / rmdir / unlink / rename +- stat / fstat / readdir +- truncate / ftruncate +- fsync / fdatasync +- flock / fcntl 文件锁 +- mmap / msync +- fallocate +- hard link / symbolic link(按 Chromium 实际需求支持) + +SQLite、LevelDB 等目录默认走 Direct Backend。不能完整支持的操作不得静默返回成功;必须返回正确错误,或通过 Direct Backend 提供原生语义。 + +## 11. 运行与管理接口 + +Storage Hook 作为 ProfileMax Node 的本地组件运行,不独立提供公网 API。 + +Node 内部接口建议: + +- `Mount(config)`:初始化 Volume、StorageFS 和 tmpfs 路径规则。 +- `ReloadConfig(config, version)`:应用新配置。 +- `GetStatus()`:返回持久化挂载、tmpfs 挂载和 Volume 状态。 +- `GetMetrics()`:返回磁盘 IO、tmpfs 使用量、容量、错误和队列指标。 +- `CreateProfile(profileID)`:创建 Profile 持久化目录及对应 tmpfs 挂载。 +- `GetProfilePath(profileID, category)`:向 Chromium、impersonate-proxy 返回受管路径。 +- `DeleteProfile(profileID)`:停止相关进程后清理 Profile 数据及 tmpfs。 +- `Flush()`:按策略刷盘;tmpfs 内容不执行持久化刷盘。 +- `Unmount()`:停止接收新 IO,完成必要刷盘后卸载。 + +Admin 通过 ProfileMax Node 现有控制通道下发配置并读取状态,不让 Storage Hook 自己连接 Admin 数据库。 + +## 12. 监控指标 + +至少提供: + +- 持久化 Volume 总容量、已分配容量、可分配容量。 +- tmpfs 总容量、每 Profile 使用量、峰值和 OOM/ENOSPC 次数。 +- Pack Segment 数量和利用率。 +- Direct Backend 文件数量与容量。 +- 按后端分类的读写吞吐量、IOPS、平均延迟、P95/P99 延迟。 +- FUSE 请求数、请求排队时间。 +- 元数据缓存命中率。 +- WAL 大小、快照耗时和恢复耗时。 +- CPU 使用率、RSS、线程数。 +- IO 错误、Volume 不可用、容量不足次数。 + +指标采集必须低频、低开销,不允许每次文件 IO 都进行复杂统计。 + +## 13. 实施阶段 + +### Phase 1:存储路由与最小可用版本 + +- 先建立原生 ext4/XFS 与 tmpfs 基准。 +- 实现 Storage Hook 的统一 Profile 路径入口。 +- 实现持久化 Direct Backend 和 tmpfs Manager。 +- 配置 Chromium HTTP Cache、Code Cache、GPU/Shader Cache 的 tmpfs 路由。 +- 将 impersonate-proxy 缓存目录纳入 Storage Hook,默认持久化。 +- 验证 Cookies、Local Storage、IndexedDB、Service Worker 等持久化数据不受影响。 +- 验证 Chromium 与 impersonate-proxy 都无法绕过 Storage Hook 写入未管理目录。 + +### Phase 2:Pack Backend + +- 通过基准测试确认 inode 压力是否值得引入 Pack。 +- 实现小文件 Pack Segment、内存索引、追加写和随机读。 +- 实现对象删除标记和后台回收。 +- 增加 Direct/Pack/tmpfs 路由规则。 +- 对比 Pack、Direct、tmpfs 与原生文件系统的 CPU、IO 延迟和文件数量。 + +### Phase 3:多 Volume + +- 实现容量权重分配。 +- 实现 Volume 状态检测和容量水位。 +- 实现配置热更新。 +- 验证单盘满载、掉盘、重启等异常场景。 + +### Phase 4:性能优化 + +- 评估 FUSE passthrough。 +- 优化请求批处理、锁竞争和元数据缓存。 +- 根据真实 Profile IO 负载调整 Pack 阈值和 Segment 大小。 +- 只有基准数据证明有收益时才增加更复杂的缓存或调度策略。 + +## 14. 性能验收 + +所有性能测试必须与原生 ext4/XFS 目录进行对照,使用相同磁盘、挂载参数和测试数据。 + +测试场景: + +1. 大量小文件创建、读取、删除。 +2. 大量小文件随机读取。 +3. 大文件顺序读写。 +4. SQLite WAL 模式读写。 +5. LevelDB 高频随机读写。 +6. Chromium HTTP Cache 写入 tmpfs 后的启动和页面加载表现。 +7. impersonate-proxy 缓存命中时,浏览器侧不再产生持久化 HTTP Cache 写入。 +8. Cookies、IndexedDB、Service Worker 等持久化数据重启后完整保留。 +9. 多 Profile 并发运行时 tmpfs 总量与单 Profile 限额。 +10. Profile 正常退出、异常退出后的 tmpfs 清理。 +11. 异常终止后的 StorageFS 恢复时间。 + +核心指标: + +- IO 吞吐量。 +- IOPS。 +- P50/P95/P99 延迟。 +- CPU 使用率和 CPU 时间/GB。 +- RSS、tmpfs 使用量和元数据内存占用。 +- 底层 inode 使用量。 +- 浏览器侧重复 HTTP Cache 的持久化写入量(目标为零)。 +- 崩溃恢复时间。 + +性能决策原则: + +- 如果 FUSE 在某类 IO 上明显慢于原生文件系统,该类文件必须走 Direct Backend 或 passthrough。 +- 如果 Pack 节省 inode 但显著增加 CPU 或 P99 延迟,应提高 Pack 适用门槛,而不是强制打包。 +- 不以单纯减少文件数量作为成功标准;IO 性能和 CPU 开销优先。 +- 不启用压缩。 + +## 15. 明确不做的事情 + +- 不使用节点数据库。 +- 不把 Admin 数据库复制到 Node。 +- 不做数据压缩或解压。 +- 不做跨 Profile 内容去重。 +- 不做内容哈希校验热路径。 +- 不把所有文件强制放进 Pack。 +- 不允许 Chromium 或 impersonate-proxy 绕过 Storage Hook。 +- 不把整个 Profile 或未经分类的网站数据目录放进 tmpfs。 +- 不在每次 IO 时请求 Admin。 +- 不在第一阶段实现复杂的自动数据均衡。 +- 不为了抽象而引入独立的存储集群服务。 + +最终交付应是一个由 ProfileMax Node 管理的本地 Storage Hook:Admin 负责配置,Node 负责执行;所有 Profile 数据统一经由 Storage Hook;持久化数据进入多磁盘 StorageFS,重复 HTTP Cache 和可重建缓存进入受限 tmpfs,impersonate-proxy 缓存也由 Hook 管理;无节点数据库、无压缩,并以 IO 性能和 CPU 效率作为主要优化方向。 diff --git a/docs/03_XLIGHTPANDA_ROLLOUT.md b/docs/03_XLIGHTPANDA_ROLLOUT.md index 11cddca..8f849b5 100644 --- a/docs/03_XLIGHTPANDA_ROLLOUT.md +++ b/docs/03_XLIGHTPANDA_ROLLOUT.md @@ -67,19 +67,22 @@ tail -f /opt/serpmax/data/logs/* # 观察 browser.started_xlightpanda / serp.po ## 5. 温池灰度现状与遗留问题(2026-11-04) -**当前舰队状态:三节点温池全部 veil/cloak 兜底(50/50、20/20、40/80),Storage Hook 三节点全启用。** xLightpanda 温池切换保留 `SERP_BROWSER_KIND=xlightpanda` 一键开关,随时可再灰度。 +**当前舰队状态:OVH-ETH-33 已开 xLightpanda 灰度(0.10.5,`SERP_BROWSER_KIND=xlightpanda`);HZ-DE-245 / OVH-DEDI-KS5B 暂保持 veil/cloak 兜底。三节点 lightpanda 0.10.5 二进制已就位;Storage Hook 三节点全启用。** ### 已突破 -- 引擎接入全链路可用:克隆启动、impersonate MITM 链、cookie 桥、入池门槛、44/50 池满、平均 RSS 75MB(veil 同池 ~450MB)。 -- Google 的 **captcha/sorry 硬门已突破**(0.10.2-0.10.4 头集修复后,首页与 hop1 均 200,不再打 /sorry 网段封)。 +- 引擎接入全链路可用:克隆启动、impersonate MITM 链、cookie 桥、入池门槛、平均 RSS 75MB(veil 同池 ~450MB)。 +- Google 的 **captcha/sorry 硬门已突破**(0.10.2-0.10.4 头集修复:UA-CH 三件套含引号、navigation accept 全段、accept-encoding 值序)。 +- **enablejs/critical-ch 软门已突破(0.10.5,2026-11-04)**:给 impersonate-proxy 加了受控转发头 dump(env 门控 `IMPERSONATE_HEADER_DUMP=<目录>`(+可选 `_FILTER=),默认零开销;源码在 /home/bigcode/data/dev/impersonate-proxy,该仓库 deployed-but-uncommitted 状态保持),veil vs xlp 同 Profile 同 IP 同 MITM 抓包对比,锁定门因: + - **Accept-CH client hints 全套缺失**。Google 响应声明 accept-ch/critical-ch 后,Chromium 对同源后续请求自动回带全部高熵提示(arch/bitness/full-version(-list)/model/platform-version/wow64/form-factors/prefers-color-scheme/downlink/rtt/ect);xlp 一概不带,Google critical-ch-check 判客户端撒谎 → enablejs 门壳 → hop2 302 /sorry。 + - 次要差异:accept-language 多了 `,en;q=0.8` 第三段(veil/Chromium 实测 `de-DE,de;q=0.9` 无英文兜底)。 + - 修复:0.10.5 Client.noteAcceptCh/appendClientHints(Accept-CH 持久化+每跳回带,值取自指纹 ctx 高熵集合)+ accept-language 去兜底。 + - 验证:ETH-33 xlp 灰度池 pagefetch `ok=true 9 结果 0.9~1.7s`(与 veil 同形);新鲜实例单测通过。 +- **已知的非引擎限制**:同一实例被连续高频手打搜索(≥7 次)后会在 navigate 回退跳吃 /sorry——频率疲劳,veil 同样如此,非引擎门;温池轮换天然规避。 -### 遗留:SERP 请求仍被 enablejs/302 门拦 -同 Profile 同 IP 同 MITM 的 A/B:veil pagefetch 1.2s 9 结果;xlp 仍 0 结果。已对齐的差异(全部实测):UA-CH 三件套(含引号)、upgrade-insecure-requests、navigation accept 全段、accept-encoding 值序(gzip 先)。MITM 对两类链路统一重写上游 TLS/H2/头序(fp.Dialer/h2fp 架构确认)。 -**剩余嫌疑(按优先级)**: -1. MITM 转发后的**实际头集/头值**尚未直接观测(httpbin 经代理链 TUNNEL 失败;echo 无法穿越代理回本机)→ 下一步:给 impersonate-proxy 加调试 dump(源码在 /home/bigcode/data/dev/impersonate-proxy),对 veil 与 xlp 各抓一次转发后头逐字段对比。 -2. xlp 页内 fetch 的 `sec-fetch-*` 组合与 veil 差异(cors/empty vs veil 同值?)——dump 后即知。 -3. hop2(sei 追加跳)的 cookie 头序/值差异。 -工具已就绪:节点上一键受控复现脚本(见 /tmp/xlp-node-repro*.ts 模式)、A/B 切换(env 一键)、HAR 级重定向链捕获(playwright response 事件)。 +### 灰度操作 +- 开:`node.env` 加 `SERP_BROWSER_KIND=xlightpanda` → `systemctl restart serpmax-node`。 +- 关:删该行 → 重启,立即回 veil/cloak(Profile 元数据不动)。 +- 二进制升级:`upstream/zig-out/bin/lightpanda`(ReleaseFast + `-Dtarget=x86_64-linux-gnu.2.36`)→ 节点 `/opt/{serpmax,profilemax}/bin/lightpanda`;先 `sudo pkill -f "lightpanda [s]erve"`(注意 pkill 模式勿自匹配 ssh 命令行,否则 ssh 会话被杀 exit 255)。 ## 6. 后续(不在本轮)