性能验证
后续调整:已按实际使用反馈撤回滚轮交互,远端网页恢复上下按钮滚动。下文保留当时性能实验记录;后台刷新、并发和截图体积优化继续保留。
2026-09-09;基线为 f3dd999,候选为本次本地优化。环境:macOS arm64、Node.js 26.7.0、Playwright 1.63.0。固定场景各重复 3 次,各配置依次执行,不同时运行其他测试。以下毫秒数据为“中位数 / 最大值”,样本量不足以估计生产 P95。
方法与边界
- 五种场景:静态、200 ms 后动态插入、10,000 px 横幅长页面、滚动后延迟插入、Cookie 保护页面。每页 12 条,额外等待统一为 300 ms(产品默认仍为 1000 ms)。
- 并发基准直接调用真实 BrowserService 和共享任务池;四个逻辑站点复用一个 loopback 测试服务器,避免修改本机 DNS。真实 hostname 的互斥另由 API 与任务池测试验证;此基准不是公网网站测试,也不是完整生产 HTTP 服务压测。
- 比对各次抓取的全部字段;仅归一化测试服务器随机端口。四种配置的 15 次结果均与基线一致,每次 12 条。图片字段、登录隔离和日期语义另由完整回归验证。
- 资源为基准 Node 进程及其 Chromium 子进程的 RSS 合计,每 200 ms 采样;RSS 合计可能重复计算共享页,不等于容器实际内存。后台资源在创建模拟前端前记录。CPU 为 ps 提供的进程生命周期平均 %CPU 的采样峰值,可超过 100%,不是瞬时 CPU 峰值。
- 编辑画面显示测试通过本机 HTTP 传输真实截图 JSON,用另一页面执行图片 decode 和两次 requestAnimationFrame;不含公网 RTT、React 渲染及反向代理影响。交互正确性由真实编辑器端到端测试覆盖。
单个订阅抓取
不含排队,包含上下文创建、加载、等待、预滚动、提取与清理;数据库写入单独测量。
| 场景 | 基线 ms | 优化后、并发 1 ms |
|---|---|---|
| 静态 | 1598 / 1735 | 1599 / 1692 |
| 动态 | 1597 / 1597 | 1565 / 1565 |
| 长页面 | 6089 / 6129 | 3689 / 3720 |
| 懒加载 | 1864 / 1896 | 1594 / 1596 |
| Cookie | 1596 / 1603 | 1597 / 1604 |
长页面收益主要来自中间滚动等待 300 → 150 ms;底部和恢复位置仍保留 300 ms,次数和时间上限不变。短页面耗时变化较小。已有滚动揭示、延迟揭示、持续增长测试通过;没有真实订阅样本,不能保证任意站点都获得同样收益。
并发、排队与资源
| 配置 | 五页整批 ms | 单次抓取 ms(混合场景) | 排队 ms | 后台 RSS 峰值 MiB | CPU 采样值 % |
|---|---|---|---|---|---|
| 基线 1 | 12756 / 12900 | 1603 / 6129 | 3199 / 11333 | 554 | 101.1 |
| 优化 1 | 10038 / 10186 | 1597 / 3720 | 3166 / 8579 | 533 | 105.1 |
| 优化 2 | 5345 / 5498 | 1605 / 3745 | 1597 / 3368 | 637 | 101.3 |
| 优化 4 | 3743 / 3885 | 1656 / 3883 | 0 / 1772 | 842 | 123.3 |
默认仍为 1。多个独立站点且内存有余量时建议先试 BACKGROUND_CONCURRENCY=2;4 的内存峰值明显更高,不作为弱机器默认值。来源集中在同一 hostname 时仍串行,不能按此表预期提速。
可视化编辑
| 指标 | 基线 PNG | 优化 JPEG 80 |
|---|---|---|
| 打开至截图返回 ms | 1592 / 1610 | 1610 / 1618 |
| 单次滚动至截图返回 ms | 117 / 129 | 142 / 173 |
| 初始截图 data URL 字节 | 102266 / 102266 | 88991 / 88991 |
| 打开与滚动两次截图累计耗时 ms | 54 / 62 | 73 / 87 |
| 本机 HTTP 传输与 JSON 解析 ms | 1 / 2 | 2 / 2 |
| 解码与画面显示等待 ms | 11 / 32 | 9 / 19 |
JPEG 降低传输体积,文字已进行视觉检查,点选坐标回归通过。截图与帧调度的耗时有波动,不承诺每帧都更快。滚轮按 50 ms 合并、请求串行、相同坐标不重复移动远端鼠标;连续滚动不会重复运行识别或预览。
分段测量
下表为 15 次后台抓取的阶段中位数;上下文指标包含 browser-ready,不应与它重复相加。browser-ready 冷启动值单独列出,暖调用复用已有进程。
| 阶段 | 基线 ms | 优化、并发 1 ms |
|---|---|---|
| 上下文创建(含浏览器准备) | 2.41 | 2.54 |
| 网络加载至 DOMContentLoaded | 8.48 | 8.50 |
| 显式配置等待 | 301.72 | 301.51 |
| 预滚动 | 1210.97 | 1209.56 |
| DOM 提取与日期上下文 | 4.85 | 5.66 |
| 入库 | 0.78 | 0.66 |
| 清理 | 2.34 | 3.00 |
首次 browser-ready:基线 111.1 ms,候选 103.5 ms。本组未设置 waitForSelector,因此选择器等待为 0;脚本支持记录该阶段。
验证与复现
- 完整单元测试 59 项通过,完整端到端测试 18 项通过;后续鼠标位置缓存改动的 4 项浏览器测试与 5 项编辑器端到端专项回归也全部通过。类型检查与生产构建通过。
- 覆盖跨站并发、同站互斥、同订阅重复请求合并、修改网址后的锁重新计算、队列屏障、失败释放容量、批量识别共享容量、Cookie 隔离与失败保留历史。
- 无键的编辑、删除等操作作为屏障;维护与关闭先停止接收任务并 drain,再恢复数据库或关闭浏览器。
PLAYWRIGHT_BROWSERS_PATH=0 pnpm test
PLAYWRIGHT_BROWSERS_PATH=0 pnpm test:e2e
pnpm build
PLAYWRIGHT_BROWSERS_PATH=0 pnpm exec tsx scripts/performance.ts > /tmp/performance-1.json
PLAYWRIGHT_BROWSERS_PATH=0 PERF_CONCURRENCY=2 pnpm exec tsx scripts/performance.ts > /tmp/performance-2.json
PLAYWRIGHT_BROWSERS_PATH=0 PERF_CONCURRENCY=4 pnpm exec tsx scripts/performance.ts > /tmp/performance-4.json比较旧版本时,把基线源码放入独立临时目录,复用相同 node_modules,并设置 PERF_SOURCE=/absolute/path/to/baseline,并发保持 1。基准只操作临时数据库与本机测试页面。所有配置应顺序运行。
每次测量的分段数据与内容摘要保存在 原始记录。
订阅列表与资源占用(2026-10-26)
本轮在 macOS arm64、Node.js 26.7.0、SQLite 3.53.4 上使用合成数据测量,不读取生产数据库或订阅。以下是局部计算成本,不代表整页加载或 HTTP 响应时间。
| 场景 | 修改前中位数 | 修改后中位数 | 方法 |
|---|---|---|---|
| 200 个订阅、6,000 条翻译的状态统计 | 334.571 ms | 1.231 ms | 5 次;增加 (feed_id, status) 索引并批量统计,逐项比对结果 |
| 1,000 行的绝对时间与相对时间格式化 | 55.998 ms | 2.387 ms | 7 次,交替顺序;复用有容量上限的 Intl 格式器,逐项比对输出 |
列表数据读取最多三次 SQL;每位所有者的检查阈值只解密一次。前端复用未变化的列表,减少重复筛选和渲染;屏幕外行由浏览器延迟布局、绘制,相对时间仍每 30 秒更新。设置页共用 30 秒内存缓存并合并同时发出的请求;保存、导入、退出登录和切换工作台会更新或清除缓存。进入设置、用户管理后暂停完整列表轮询。
资源改动与验证:
- 无编辑会话和抓取任务时,Chromium 空闲 60 秒后退出,下次请求等待旧进程关闭再启动。真实浏览器测试覆盖编辑中保持可用、空闲退出、再次抓取;这降低空闲占用,不承诺降低复杂页面的抓取峰值。
- 同一翻译缓存片段的保留时间每分钟最多写一次。连续 1,000 次缓存命中不再改变数据库版本,一分钟后首次命中更新一次。条目、日志和翻译缓存的既有保留上限,以及累计采集和翻译计数均保持原有逻辑。
- 使用相同 Node 基础镜像和锁定依赖版本,均排除本地开发目录后,在 Docker Desktop Linux arm64 上比较旧构建与新构建。新镜像仅带 Chromium 无头版本,清除安装缓存,React、React DOM 和图标库只参与前端构建。OCI gzip 压缩层合计从 620,668,779 → 345,917,082 字节(约 621 → 346 MB,减少 44%);此值来自镜像层清单,不含共享已有层时节省的下载量。
docker image ls显示的本地SIZE从 2.31 GB → 1.33 GB,约减少 42%,不包括构建缓存和数据卷。构建上下文同时排除.claude、.codex、.agents、Git 目录和本地环境变量文件。 - 新镜像以非 root 用户运行,保留 Chromium 沙箱,在 1 CPU、512 MiB 内存、512 MiB 内存加交换空间总上限、256 MiB 共享内存下通过
scripts/container-smoke.ts:网页静态资源、CLI 启动、识别四条测试内容、创建订阅、输出 RSS、数据库重开后仍保留内容。cgroupmemory.peak为 385,236,992 字节,约 367 MiB。另在正式容器网页验证设置渲染、修改检查阈值、刷新后保留和聚合弹窗,没有页面脚本错误。此结果只适用于这个小型测试页面,不是复杂网站或大量并发的最低配置保证。 - 本地完整单元与集成测试 313 项、页面回归 58 项通过。页面回归包含 1,000 个订阅的显示与搜索、相对时间继续更新、设置请求合并、栏目加载文字、个人失败阈值与聚合搜索全选。
复现局部基准:
pnpm exec tsx scripts/translation-stats-performance.ts
pnpm exec tsx scripts/ui-format-performance.ts复现限额容器验证(只使用临时容器内的数据,不挂载现有数据卷):
docker build -t feedlantern:resource-check .
docker run --rm --init --cpus=1 --memory=512m --memory-swap=512m --shm-size=256m \
--security-opt seccomp=seccomp_profile.json \
feedlantern:resource-check pnpm exec tsx scripts/container-smoke.ts本轮不提高数据库 schema 版本,仍为 23;索引在启动时幂等创建,个人检查阈值随原有加密设置保存,旧设置自动使用默认 3 次,无需手工迁移。