Browserless 本质上就是把无头 Chrome 打包成一个你自己托管的服务。它的 Docker 容器会一直跑着,通过 HTTP 或 WebSocket 接收任务,并统一执行同一套准入限制,而不是把浏览器当成每次调用时单独拉起的东西。就这次 REST 测试来看,这个服务在空闲时 没有任何 Chrome 进程,在请求执行期间会创建 Chrome 进程,结束后又回到 0。这里真正被“池化”的是服务容量和排队机制,而不是一组已经预热好的浏览器进程。
我用 v2.55.0 做了一轮受控的本地测试——把启动拆成多个阶段,在三组配置下验证准入控制,拿已知真值校验端点一致性,做了 30 次会话的持续压测,还测了超时边界。真正有价值的不是速度提升,而是运行层面的表现:准入上限和客户端看到的响应完全一致,而那些棘手问题大多出在部署层面。
最有意思的结果并不是某个延迟数字。Browserless 并不会让 Chrome 启动得更快——它是在 Chrome 前面加了一道门禁:里面固定只允许若干会话,后面排队,超出的人直接返回 HTTP 429。当我改了两个环境变量后,这个上限从 4 变成 8,再变成 10,而容器自身的统计结果和我客户端看到的状态码在每一次请求上都一致。
Browserless 到底是什么
很多人会在这里搞混分类。Browserless 不是一个你导入后直接调用的库。它是一个 Docker 镜像——ghcr.io/browserless/chromium——你把它当作一个长驻服务来运行。它负责浏览器任务的调度,并通过两种方式对外提供能力:REST 端点(/content、/scrape、/screenshot、/pdf,以及 /function 和 /unblock)和 CDP/WebSocket 接口,Puppeteer 与 Playwright 可以 connect() 上去。
我测试的是 REST 接口。WebSocket 这条路径确实存在,也被广泛使用,但这次没有测它。
本次测试版本是 v2.55.0,核验时间为 2026 年 7 月 27 日。
| 项目 | 值 |
|---|---|
| 镜像版本 | v2.55.0,发布于 2026 年 7 月 14 日 |
| Chrome | 149.0.7827.0 |
| Node | 24.18.0 |
| 基础镜像 | Ubuntu 24.04 |
| GitHub stars | 截至 2026 年 7 月 27 日约 13,525 |
星标数会波动;这里仅代表某一时点的数据。
许可证门槛:SSPL-1.0 或商业授权
该仓库提供 SSPL-1.0 或 Browserless 商业许可证 两种选择。在决定之前,请务必阅读当前最新的 仓库 LICENSE 和 Browserless 的 官方开源部署说明。本文没有对商业产品、闭源应用、CI 系统、托管服务或内部部署做法律分析,因此不会替这些场景判定许可证适用性。请由法务或负责软件许可的人来评估你的部署与分发模式。
Browserless 也提供托管套餐。价格和使用量单位会变动,而且不属于这次自托管测试范围,所以请以官网最新信息为准,不要把文中的过时表格当成采购依据。
会话模型在底层是怎么运作的
这条被测的 REST 路径表现得像是“每次请求都启动一次浏览器任务”,但这个测试框架并没有深入追踪 Browserless 内部实现,因此无法区分“新开浏览器进程”和“复用某种上下文”这两种可能。它能确认的事情更简单:空闲时 Chrome 进程为 0,请求进行中会出现 11 个 chrome 家族进程,顺序跑完后又回到 0。没有证据表明在这个配置下存在预热浏览器池。
这个长驻 Node 服务会接纳有限数量的浏览器任务,把另一部分任务放进队列,然后拒绝其余请求。下面测到的行为契约,就是这个准入模型本身。不要因为“pool”这个词就推断存在进程或上下文复用;这套时间测试无法证明这一点。
准入控制有两个参数:
CONCURRENT—— 同时运行的会话数。QUEUED—— 还能排队等待的额外请求数。
容器的 /config 端点显示默认值为 CONCURRENT=10、QUEUED=10、TIMEOUT=30000。超过 CONCURRENT + QUEUED 的请求会被立即拒绝。
认证不是可选项。Browserless v2 始终需要 token——如果你不设置 TOKEN,它会在启动时自动生成一个随机值并输出到标准输出。每个 REST 请求都要带上 ?token=。
在可观测性方面,你可以拿到 /pressure(运行中、排队中、CPU、内存、最近被拒绝请求),还有 /sessions 和 /config。另外还有一个 /metrics 的 JSON 导出,但它需要设置 METRICS_JSON_PATH,而我这次没有用到。镜像使用 dumb-init 作为 PID 1,这也是官方对容器化 Chrome 长期以来“僵尸进程”问题的处理方案。
安装现实:一条命令,以及没人会写进那条命令里的四件事
大家常引用的安装方式确实只是一条 docker run。但你真正要规划的是它周边的东西。
这个镜像有 4.34 GB。 这才应该影响你的预期,而不是启动延迟。镜像清单同时包含 linux/arm64 和 linux/amd64;在我的 arm64 主机上,Docker 拉取的是原生 arm64 版本。(容器里的 Chrome 用户代理仍然显示 X11; Linux x86_64——那只是 Chrome 在 Linux 上的外观 UA,不代表模拟运行。uname -m 显示的是 aarch64。这个问题经常被误报。)
我所有测量都使用了 --shm-size=2g。 这个测试框架没有在 Docker 默认 /dev/shm 下做对照,因此本文不能说 2 GiB 在所有情况下都是硬性要求,也不能精确量化失败阈值。你应该根据浏览器数量和工作负载来设置它。
token 是部署问题,不是走过场。 没有它,任何能访问 3000 端口的人都能在你的网络里占用浏览器资源。
容器网络是你自己的责任。 我的测试目标跑在宿主机上,所以容器通过 host.docker.internal 访问它(colima 用 --add-host host.docker.internal:host-gateway 做了映射)。在相信任何结果之前,我先用原始 curl 确认容器真的能访问这个测试目标。
我的环境是:macOS 26.5.2 arm64 上的 colima 0.10.3(6 CPU / 11.6 GiB)和 Docker 29.2.1。测试框架只用了 Python 3 标准库。对于 PNG 和 PDF,我检查的是文件签名,而不是解码有效性、尺寸、页数、完整性或视觉准确性。
最小可用启动命令就是下面这样:使用固定版本镜像、显式 token,以及这次测试用到的共享内存大小:
docker run --rm -p 3000:3000 --shm-size=2g \
-e TOKEN=replace-with-a-secret \
ghcr.io/browserless/chromium:v2.55.0
当 /pressure?token=... 有响应后,带着目标 URL 的 JSON 体向 POST /content?token=... 发送一次已认证请求,就能把 REST 路径跑起来。生产环境调用还需要对 429 做带抖动的有限重试;如果立刻重试,只会和同样已经满了的队列继续抢位。
启动成本,拆开看

三次全新的 docker run 启动,以下为中位数及最小-最大范围:
| 阶段 | 中位数 | 范围 | 实际代表什么 |
|---|---|---|---|
docker run → /pressure 返回 200 | 0.78 s | 0.70–0.87 s | HTTP 端点已可响应;此检查并未验证浏览器是否真正完成启动 |
就绪 → 首次 /content 渲染 | 0.32 s | 0.28–0.41 s | 观察到的首次请求:浏览器任务 + 跳转 + 返回 HTML |
后续 /content 调用 | 0.15 s | 0.147–0.154 s | 同一容器内后续请求的观测延迟 |
中间这一行很容易被过度解读。它不能单独衡量浏览器启动,也不能证明 Browserless 比进程内库启动 Chrome 更快。 它只是一次 HTTP 往返进入容器,再加上浏览器处理、页面跳转和响应传输。首次与后续请求之间大约 0.17 秒的差异,可能包含文件系统、操作系统、Chrome、Node 或容器缓存的影响。由于空闲时 Chrome 进程为 0,而这几次调用没有采集 CDP trace 或进程时间线,所以无法把这个差异归因于浏览器复用或“摊薄”了启动成本。
另外,这些数字是在 macOS 上的 colima 虚拟机里测出来的。裸机 Linux 的结果会不同。不要把 0.78 秒直接拿去跟 SRE 说成可移植的结论。
实测:从两边一起找上限
CONCURRENT + QUEUED → 429 这个契约被到处提起,却很少有人真正验证。
测试方法是:用一个测试路由让服务端睡 5 秒,这样每个请求都会稳定占住一个会话一段已知时长。然后同时发出 CONCURRENT + QUEUED + 4 个请求,观察返回结果——同时再让一个独立采样线程轮询 /pressure,读取容器自己的统计值。
| 配置 (CONCURRENT, QUEUED) | 发出请求数 | HTTP 200 | HTTP 429 | 服务器 /pressure 峰值(running / queued / recentlyRejected) |
|---|---|---|---|---|
| (2, 2) | 8 | 4 | 4 | 2 / 2 / 4 |
| (3, 5) | 12 | 8 | 4 | 3 / 5 / 4 |
| (5, 5) | 14 | 10 | 4 | 5 / 5 / 4 |
这里有三点结论。
上限每次都严格等于 CONCURRENT + QUEUED。成功返回数分别是 4、8、10,也就是每组配置下的设定总和。被拒绝的数量等于超出的部分,三次测试里都是 4。
这个上限是会变化的。它不是写死在镜像里的常量,而是你配置多少就是多少。通过改环境变量让它从 4 → 8 → 10,这正是它有用而不是只剩下“参数知识”的原因。
而且这两个信号彼此独立。我的客户端状态码来自真实 HTTP 响应;/pressure 来自容器内部的统计,由另一个线程轮询得到。在这三次短测中,它们完全一致。这让 /pressure 成为一个可以考虑用于生产监控的信号,但还不能直接当作完整的自动扩缩容契约:轮询频率、重置语义、多副本聚合,以及更长时间混合负载下的表现,都还需要继续验证。
还有一个容易被通过/失败计数掩盖的细节。排队的请求并不会失败——它只是等待,而且可能会等很久。 在 (2, 2)、每次工作 5 秒的条件下,成功响应到达时间分布在 5.7 秒到 11.0 秒之间,中位数 8.3 秒。因此端到端延迟大约会达到两个会话时长。这个框架没有分别记录“准入时间”和“执行时间”,所以无法把全部延迟完全归因于排队等待。
放到真实任务里会是什么样
假设你每天晚上要把 4,000 个商品页面渲染成 PDF,每个页面大约要 5 秒。你设置 CONCURRENT=5, QUEUED=5。那么吞吐上限就是每 5 秒 5 个页面,也就是每秒 1 个页面;如果你能让管道始终保持满载,这个任务大约要 67 分钟。这个算式是基于实测行为推出来的,不是正式 benchmark,但在你上线前,这就是应该做的那种算账。
任何在所有运行槽和排队槽都被占满后到达的请求,都可能立即收到 429;同时发射请求并不保证谁会输掉这个竞争。任务执行器应该把这个响应视为背压信号,并使用带抖动的有限重试。否则它可能会悄悄丢页面,而上层任务账目还以为一切正常——这是一个运维风险,而不是这个测试框架证明过的故障场景。
实测:端点到底看到了什么
为了诚实地测试渲染准确性,测试页会把标记文本隐藏起来,只对真正运行浏览器的人可见。可见字符串 Runtime Injected Marker 88 是在页面加载时由 JavaScript 片段拼出来的,因此服务器发送的任何字节里都不存在它的连续字面量。直接静态抓取该页面只会得到 702 字节,而且不包含这两个标记。
| 端点 | 结果 | 字节数 |
|---|---|---|
/content | 运行时注入的标记存在,并且包含两个静态标记 | 811 |
/scrape 作用于 #scrape-me(JS 注入节点) | 返回 SCRAPE_TARGET_VALUE_CC | 422 |
/screenshot | 响应包含 PNG 签名 89 50 4E 47 | 18,621 |
/pdf | 响应包含 PDF 签名 %PDF- | 40,974 |
| 四个端点都不带 token | HTTP 401(不是 403) | — |
/content 返回 811 字节并且包含注入标记,说明页面在 HTML 返回之前确实由真实 Chromium 渲染过。/scrape 则从一个只有 JavaScript 执行后才出现的节点里抓到了值。两者都在没有任何客户端自动化代码的情况下完成——只需要一次带认证的 POST。
这才是它真正的卖点。在同一轮测试里,静态爬虫完全漏掉了这类内容;而进程内浏览器库(chromedp、rod、Selenium)只有在我显式写了等待逻辑后才抓到。Browserless 用一个 curl 风格的请求就抓到了。你换来的是把自动化代码交给部署层去承担。
这个说法也有两个边界。这里的证据只覆盖了我测试页里的内容类型,并不代表对整个现代网页的全面调查。还有 /unblock 这个反检测端点,我是刻意没碰的——因此这些结果不能被解读为反爬能力的证明。/function、/download 和 /performance 也都没有测试。
实测:短期残留检查
容器化 Chrome 一直有“留下僵尸进程”的名声,所以我用 CONCURRENT=3 连续跑了 30 次会话,并统计容器内部的进程数量。
在相信结果之前,我先校准了检测器。会话进行中时,/proc 枚举器读到了 11 个 chrome 家族进程(browser、zygote、GPU、renderer、utility 等)。这很重要:它证明这个工具确实能看见 Chrome,因此后面的 0 才是测量结果,而不是“看不见”。如果一个泄漏测试只会报“0 个进程”,却不能证明自己能数进程,那就毫无价值。
30 次会话结束后:0 个 chrome 进程,0 个僵尸进程。 唯一还在的进程是 dumb-init、node、Xvfb、start.sh 和 sh。空闲时 /sessions 读数为 0。
来自 docker stats 的容器内存(这是运维视角的数字,不是单个进程的 RSS):
| 第 N 次会话后 | 0 | 5 | 10 | 15 | 20 | 25 | 30 |
|---|---|---|---|---|---|---|---|
| 容器内存 (MiB) | 294 | 300 | 301 | 302 | 302 | 303 | 303 |
30 次会话的净增长约 9.5 MB,而且在第 10 次之后曲线就基本平台化了。这和一个简单的、按会话线性增长的泄漏模式不一致。Node 预热是一个合理解释,但仅凭这组进程和内存数据无法证明。
范围说明:30 次顺序会话只是一次小规模浸泡测试,不是耐久性测试,也不是并发测试。问题追踪里长期存在的 EventEmitter 监听器警告,才是那种可能在数小时、上千次会话后才显现出来的东西,而我这次没有跑到那个程度。这里能得出的结论只有:在 v2.55.0 上、这个窗口里,没有观察到 Chrome 进程或僵尸进程的累积。
超时边界
TIMEOUT 文档里说这是个可调参数。我想看看它会不会真的触发。
| 情况 | 页面占用时长 | 状态 | 耗时 |
|---|---|---|---|
| 预算内 | 2,000 ms | 200 | 2.406 s |
| 超出预算 | 15,000 ms | 408 | 5.007 s |
当 TIMEOUT=5000 时,一个试图让页面保持 15 秒的会话会在 5.007 秒时返回 HTTP 408,而不是一直挂住。这个单次观察足以证明它在接近配置边界时确实会执行超时限制。但它并没有揭示定时器实现,也没有证明槽位是否被正确释放;更强的测试应该重复这个试验,观察 /sessions 和 /pressure 回到空闲,然后再确认后续请求能拿到被释放的槽位。
迁移陷阱:PREBOOT 是无声失效,而且它不会告诉你
如果只让我在升级前拿到一个结果,我最想看到的就是这个。
Browserless 2.0.0 移除了 PREBOOT 和 KEEP_ALIVE——更新日志 说它们之所以被删,是因为容易让人误解、作用不大,而且还会引发 bug。这个决定挺合理。问题在于,当 v1 配置被原封不动地复制到 v2 时会发生什么,而这恰恰是大多数人升级时最常见的做法。
我用 -e PREBOOT=true 启动容器,并把它和默认情况做了对比:
| 信号 | PREBOOT=true | 默认值,未设置该标志 |
|---|---|---|
| 就绪时间 | 0.716 s | 0.776 s |
| 冷渲染 | 0.314 s | 0.318 s |
| 热渲染 | 0.163 s | 0.150 s |
| 空闲时 Chrome 进程数 | 0 | 0 |
所有时间都落在默认组自己的最小-最大范围内——这只是噪声,不是效果。并且没有任何预热:PREBOOT=true 的容器在空闲时根本没有持有浏览器,和未设置该标志的容器完全一样。还有两个信号,也都不是数字本身:
/config根本没有preboot这个键。已有的键是concurrent、queued、timeout、token、maxCPU、maxMemory、retries等等。- 没有报错。没有警告。容器日志里什么都没有。
所以,v1 里的 PREBOOT 配到 v2 上,在普通启动和日志检查中会悄悄变成无效操作。可检测的信号是:/config 里缺少这个键,而且行为没有任何变化;Browserless 不会输出明确的拒绝信息或警告。迁移检查应该去核实实际生效的配置,而不是把“启动成功”当成所有环境变量都生效了的证明。
KEEP_ALIVE 则是另一种情况,这两个不能混为一谈。 它同样在这个版本里被移除了,但它并不是静默失效——我简单探测了一下容器,发现它会在 stdout 里直接输出 Environment variable of "KEEP_ALIVE" is deprecated and ignored.。这是非常标准的运维可见警告。我没有像 PREBOOT 那样用同一套测量框架去完整测试 KEEP_ALIVE,所以这里把它作为一次检查而不是测量结果来报告。但方向已经足够清楚:只有 PREBOOT 是那个无声陷阱。 相比“v2 会忽略你所有 v1 标志”这种笼统说法,Browserless 对 KEEP_ALIVE 的态度其实更诚实。
优缺点
优点
- 准入控制表现与文档完全一致,而且会随着配置变化——在三种不同上限下,客户端状态码和服务器自身统计都同时验证通过。
/pressure在三次短跑中与客户端可见的 running、queued、rejected 计数一致;可以把它当作自动扩缩容和告警的候选输入之一。- 真正的 Chromium 渲染,而且不需要客户端自动化代码:一次带认证的 POST 就能拿到静态抓取看不到的 JS 注入 DOM。
- 在 30 次顺序会话中没有观察到 Chrome 进程累积;结束后 Chrome 进程数为 0,僵尸进程也为 0。
- 一次
TIMEOUT测试在 5.000 秒预算下于 5.007 秒返回 408;清理和槽位释放没有单独验证。 - 默认开启认证:没有 token 时,四个 REST 端点都返回 401。
- 一条
docker run就能把服务拉起来,大约 0.78 秒可达就绪状态,随后首次渲染约 0.32 秒。
缺点
- 镜像 4.34 GB。 这是最诚实的成本标题,会体现在你的镜像仓库、CI 缓存和冷部署时间里。
- SSPL-1.0 或 Browserless 商业许可证。 需要结合你的部署和分发模式,逐条核对当前条款。
- 来自 v1 的
PREBOOT在 v2 中会被接受但静默忽略——没有报错,没有警告,也没有/config键。 - 你运维的是一个服务,而不是简单加了一个依赖:要管容器、token、网络路径、准入上限,以及升级责任。
- 在
(2, 2)的测试里,排队请求把端到端延迟推高到大约两个会话时长;我们没有单独测出排队等待时间。 - 这次测到的 REST 延迟包含 HTTP 往返,不足以单独说明浏览器启动成本,也不能证明浏览器复用。
谁适合用,谁不适合
当不止一个地方需要浏览器时,Browserless 的价值就出来了。多个应用共享一个渲染服务、一个团队想把截图和 PDF 放到 HTTP 端点后面,而不是让每个服务都依赖 Chrome、或者一个任务管道确实需要可测的容量上限和背压——这种场景非常适合它。如果你已经在跑 Docker,而且有人负责部署,那它的运维逻辑很清楚:准入可预测、背压可观测,而且在 30 次顺序检查里没有观察到 Chrome 进程或僵尸进程累积。
如果你的替代方案是让栈里的每个服务都自己装一份 Chromium,那把这些东西集中到一个带 token 和上限的容器里,确实是个合理的架构取舍。
如果你只是在写一个脚本,就别用它。为了让一个 Python 文件抓取渲染后的页面就去拉 4.3 GB、再启动一个容器,仪式感太重了——进程内的浏览器库就能完成同样的事,而且不用单独部署服务。如果你的商业产品不接受 SSPL 条款,而且也没法解决,那就别用它。如果你真正想要的是“自带预热、没有冷启动成本”的浏览器,那 v2 的 PREBOOT 不会给你这个。如果你真正的问题是反爬处理,那也别用它,因为那在一个我故意没测的端点里,我不会替它背书。
替代方案,以及 Thunderbit 的位置
更值得比较的不是 Browserless 和另一个容器,而是浏览器到底跑在哪里、谁来负责让它活着。
相关阅读:Browsertrix Crawler 评测。
相关阅读:chromedp 评测。
| 浏览器库(chromedp、rod、Selenium、Playwright) | 自托管 Browserless | Thunderbit 托管提取 | |
|---|---|---|---|
| 浏览器运行位置 | 在你的进程内 | 在你的容器里 | 在别人的基础设施上 |
| 启动成本 | 安装依赖包 | 4.3 GB 镜像 + 容器 + token | API key |
| 这里测到的时间 | 本文未测 | HTTP 就绪后首次渲染 0.32 s;后续调用中位数 0.15 s | 本文未测 |
| 你要写什么 | 带显式等待的自动化代码 | 一次带认证的 POST | 一次 HTTP 调用 |
| 返回什么 | 你自己脚本决定 | HTML、按签名匹配的 PNG/PDF 响应、抓取到的节点 | 产品特定的结构化 JSON 或 Markdown |
| 容量上限 | 你的机器 | CONCURRENT + QUEUED,然后是 429 | 服务商套餐 |
| 谁来值班 | 你 | 你 | 对方 |
如果你想让浏览器跑在自己的进程里,而且不介意自己写等待逻辑,那浏览器库更轻,没有额外部署。我在 Playwright 与 Puppeteer 对比 和更广泛的 开源爬虫项目盘点 里写过那一侧的内容。
如果你根本不想运维浏览器,我们自己的 Thunderbit 就是一个托管替代方案。Browserless 返回的是你自己代码再去解释的渲染结果;Thunderbit 可以直接返回 Markdown 或与 schema 匹配的数据,而渲染基础设施由服务商负责。本文没有对 Thunderbit 的延迟、容量、失败行为、提取质量或成本做基准测试,所以这里的表格描述的是责任边界,而不是性能对比。
同一轮测试里的相关阅读还有:Crawl4AI 评测与替代方案 介绍了你自己运行的浏览器驱动 Markdown 流水线,而 网页爬虫工具总览 则梳理了更大的类别。
结论
要不要运行 Browserless?如果有多个调用方需要浏览器能力、有人能运维这个容器,而且你的许可证审查也认可这种部署模式,那么答案是要。在三组合成准入测试中,接受数量都等于 CONCURRENT + QUEUED,超出的部分返回 429,而 /pressure 与客户端可见计数保持一致。在另一项 30 次顺序会话检查中,没有观察到 Chrome 进程或僵尸进程累积。一次超时测试则在接近配置边界时返回了 408。这些都是有价值的有限观察,但不是普遍保证。
但也要诚实地看清代价。它是一个 4.34 GB 的镜像,也是你要自己运维的服务,不是一个简单加进来的依赖;这次测试并没有证明它比进程内浏览器库更快。它带来的好处是,你可以对浏览器进行配额控制:有明确上限,也有可测的背压。在 30 次顺序检查里,没有观察到 Chrome 进程或僵尸进程累积。代价是部署负担和你必须认真阅读的许可证。如果你只是从一个脚本里渲染几页内容,这笔账不划算。如果你在运维一个多个服务都依赖的渲染层,那它就值;只是上手前别忘了检查你从 v1 迁过来的环境变量,因为 PREBOOT 会一副“我在正常工作”的样子,但实际上什么也没做。
试试 Thunderbit 做网页数据提取 Get Started Free
常见问题
Browserless 会让无头 Chrome 更快吗?
这次测试无法回答这个问题。HTTP 端点在 docker run 之后 0.78 秒变得可用;随后第一次 /content 调用耗时 0.32 秒,同一容器中的后续调用大约 0.15 秒。这些数字把 HTTP 往返、浏览器工作、跳转和响应传输都算进去了。测试框架没有单独提取启动时间,没有追踪进程复用,也没有给出可比较的进程内基准。你应该把 Browserless 用作共享服务边界和准入控制,然后再为自己的延迟路径做基准测试。
超过 Browserless 的并发上限会怎样?
会立刻得到 HTTP 429。这个上限严格等于 CONCURRENT + QUEUED,我在三组配置下都确认过:(2,2) 接受 4 个、拒绝 4 个;(3,5) 接受 8 个、拒绝 4 个;(5,5) 接受 10 个、拒绝 4 个。服务器的 /pressure 每次都显示匹配的 running、queued 和 recentlyRejected 数量。值得注意的是,排队请求不会失败,它们只是等待——在 (2,2)、5 秒工作量的情况下,成功响应花了 5.7 秒到 11.0 秒。客户端应当把 429 视为背压,并配合重试和退避。
Browserless v2 里 PREBOOT 还有效吗?
无效。PREBOOT 在 2.0.0 中被移除了,而 v2 会接受 -e PREBOOT=true,既不报错也不警告,但它并不会真正做任何事。我从三个角度确认了它是无效的:延迟和默认情况没有区别,空闲的 PREBOOT=true 容器里没有等待中的 Chrome 进程,而且 /config 里根本没有 preboot 这个键。如果你迁移了 v1 配置,你的实例并不会预热。顺便说一句,同一版本里移除的 KEEP_ALIVE 会打印“deprecated and ignored”的警告——所以静默失效的问题是 PREBOOT 独有的。
Browserless 商用是免费的吗? 仓库提供 SSPL-1.0 或 Browserless 商业许可证,但本文没有把具体的商业或闭源场景映射到某一种选项。请先阅读当前的 LICENSE 和 官方部署说明,再让负责软件许可的人评估你的部署和分发模式。
Browserless 会留下僵尸 Chrome 进程吗?
在这次测试的短窗口里没有累积。30 次顺序会话后,容器里是 0 个 Chrome 进程、0 个僵尸进程,只剩下 dumb-init、node、Xvfb、start.sh 和 sh。会话进行时,检测器能数到 11 个 chrome 家族进程,所以它不是“看不见”。容器内存从 294 MiB 增长到 303 MiB 后在样本里趋于平稳。这不是多小时、并发或上千次会话的耐久结果。


