跳转到内容

Artisan 故障排查

每一种 bridge 失败都会以带类型的异常呈现。将异常和消息对照下方表格。每一行都指向确切的源码检查,帮助你修正成因,而不是只处理症状。

chrome-php/chrome is not installed. Install it via: composer require chrome-php/chrome:^1.15

CDP 客户端库未进入 autoloader。BrowserPool::getBrowser() 会在任何一次 Chrome 启动之前就抛出这个异常。运行提示中的安装命令。这与缺少二进制文件不同:库检查和二进制文件检查是两个相互独立的阶段。

Chrome renderer failed: <cause>

Chrome 无法启动、超时,或已经崩溃。前一个异常会保留原始成因。常见成因:

  • **找不到二进制文件或无法执行。**用 chromium --headless --dump-dom about:blank 验证。将 chrome_binary 设为绝对路径。
  • **Sandbox 无法初始化(容器环境)。**原因中会提到 sandbox 或 namespace。配置一个支持 sandbox 的容器(推荐做法),或先阅读 /integrations/artisan/security-and-operations/ 一节,再设置 no_sandbox: true
  • **超时。**某个较重的文档超过了 render_timeout。针对该工作负载调高超时,或缩减文档;在面向用户的路径上,需要权衡 denial-of-service 的取舍。
  • **缺少共享库。**Chrome 会立即退出。安装该发行版所需的 Chrome 依赖集。

Chrome printToPDF returned empty data

Chrome 已启动,但 printToPDF 返回了零字节。常见成因是输入未产生任何可渲染的框(body 为空、所有内容均为 display:none),或 Chrome 在打印途中崩溃。确认该 HTML 会渲染出一个可见的框。检查主机的内存压力。

RuntimeException — 在调用 Chrome 之前就拒绝输入

标题为“RuntimeException — 在调用 Chrome 之前就拒绝输入”的章节
消息包含成因修正方式
exceeds maximum allowed sizeHTML 超过 maxHtmlSize缩减输入,或调高 max_html_size(会扩大资源耗尽的攻击面)
oversized base64 data URI内嵌的 base64 ≥ 13 MB缩小内嵌资产;改为引用较小的图片
forbidden meta refresh redirect<meta http-equiv="refresh"> 存在移除该标签;它是可导向 SSRF 的导航向量,一律会被拒绝

这些都来自 ChromeSecurityPolicy::validate(),并且会在调用 Chrome 之前触发,因此测试中可以快速、低成本地命中。

Page <n> has no content stream

Chrome 产生了一份 parser 无法从中提取页面的 PDF。这种情况很罕见,通常意味着 Chrome 输出有缺陷,或某个 Chrome 版本产生了非预期的结构。记录 Chrome 版本和该输入。确认该二进制文件是受支持的 Chrome/Chromium 构建。

这不是 bug。bridge 会拦截每一次子资源抓取(CSP default-src 'none' 再叠加一道 CDP setBlockedURLs('*') 拦截)。远程的 <img>、样式表、字体、脚本和 iframe 都不会加载。将资产以内嵌 data: URI 的形式提供,并通过 defaultCss<style> 内嵌 CSS。请参阅 /integrations/artisan/security-and-operations/ 一节中的网络模型。

文档溢出到了 Chrome 的第二页,而 bridge 只导入了第 0 页。原因要么是自动适配高度的缓冲对异常高的回流而言太小,要么是显式指定的高度太小。提供一个按内容大小设定的显式高度,或移除显式高度,改用带安全缓冲的自动适配高度。请参阅 /integrations/artisan/production-usage/ 一节中的高度处理。

这是预期行为。BrowserPool 每 100 次渲染就重启 Chrome 一次,以限制内存用量。一条 notice 级别日志会记录这次重启及对应的渲染次数。在 SLO 中应将它视为已知的周期性成本,而不是事故。重启频率高于每 100 次渲染一次,说明文档比预期更重。

BrowserPool 通过每 100 次渲染重启一次来限制增长,但一个长期存活的 worker 仍可能累积内存。在大批次之间调用 close() 以提早回收 Chrome 进程,并让该 worker 在主机内存限制下运行。

  1. 用一段最小可信 HTML 片段重现问题,以便把输入与环境隔离开来。
  2. 以 worker 用户身份,在主机上运行 chromium --headless --dump-dom about:blank
  3. 注入一个 PSR-3 logger,并读取 info/notice 级别的日志(二进制文件路径、重启次数)。
  4. 确认 chrome-php/chrome 已安装:如果 BrowserPool::getBrowser() 不抛出 ChromeNotAvailableException,即表示已安装。
  5. 查看该异常的前一个异常,找出底层的 Chrome 成因。
  • 安装指南:/integrations/artisan/install/
  • 配置:/integrations/artisan/configuration/
  • 安全与运维:/integrations/artisan/security-and-operations/
  • Chrome 渲染器设置:/integrations/artisan/chrome-renderer-setup/
  • 生产环境使用:/integrations/artisan/production-usage/