Artisan 故障排查
每一种 bridge 失败都会以带类型的异常呈现。将异常和消息对照下方表格。每一行都指向确切的源码检查,帮助你修正成因,而不是只处理症状。
按异常诊断
标题为“按异常诊断”的章节ChromeNotAvailableException
标题为“ChromeNotAvailableException”的章节
chrome-php/chrome is not installed. Install it via: composer require chrome-php/chrome:^1.15
CDP 客户端库未进入 autoloader。BrowserPool::getBrowser() 会在任何一次 Chrome 启动之前就抛出这个异常。运行提示中的安装命令。这与缺少二进制文件不同:库检查和二进制文件检查是两个相互独立的阶段。
ChromeRenderException — 启动或超时
标题为“ChromeRenderException — 启动或超时”的章节
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 依赖集。
ChromeRenderException — 空白输出
标题为“ChromeRenderException — 空白输出”的章节
Chrome printToPDF returned empty data
Chrome 已启动,但 printToPDF 返回了零字节。常见成因是输入未产生任何可渲染的框(body 为空、所有内容均为 display:none),或 Chrome 在打印途中崩溃。确认该 HTML 会渲染出一个可见的框。检查主机的内存压力。
RuntimeException — 在调用 Chrome 之前就拒绝输入
标题为“RuntimeException — 在调用 Chrome 之前就拒绝输入”的章节| 消息包含 | 成因 | 修正方式 |
|---|---|---|
exceeds maximum allowed size | HTML 超过 maxHtmlSize | 缩减输入,或调高 max_html_size(会扩大资源耗尽的攻击面) |
oversized base64 data URI | 内嵌的 base64 ≥ 13 MB | 缩小内嵌资产;改为引用较小的图片 |
forbidden meta refresh redirect | <meta http-equiv="refresh"> 存在 | 移除该标签;它是可导向 SSRF 的导航向量,一律会被拒绝 |
这些都来自 ChromeSecurityPolicy::validate(),并且会在调用 Chrome 之前触发,因此测试中可以快速、低成本地命中。
PdfParseException
标题为“PdfParseException”的章节
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/ 一节中的高度处理。
每约 100 次渲染会出现一次延迟峰值
标题为“每约 100 次渲染会出现一次延迟峰值”的章节这是预期行为。BrowserPool 每 100 次渲染就重启 Chrome 一次,以限制内存用量。一条 notice 级别日志会记录这次重启及对应的渲染次数。在 SLO 中应将它视为已知的周期性成本,而不是事故。重启频率高于每 100 次渲染一次,说明文档比预期更重。
内存在长批次处理中逐渐增长
标题为“内存在长批次处理中逐渐增长”的章节BrowserPool 通过每 100 次渲染重启一次来限制增长,但一个长期存活的 worker 仍可能累积内存。在大批次之间调用 close() 以提早回收 Chrome 进程,并让该 worker 在主机内存限制下运行。
诊断检查清单
标题为“诊断检查清单”的章节- 用一段最小可信 HTML 片段重现问题,以便把输入与环境隔离开来。
- 以 worker 用户身份,在主机上运行
chromium --headless --dump-dom about:blank。 - 注入一个 PSR-3 logger,并读取
info/notice级别的日志(二进制文件路径、重启次数)。 - 确认
chrome-php/chrome已安装:如果BrowserPool::getBrowser()不抛出ChromeNotAvailableException,即表示已安装。 - 查看该异常的前一个异常,找出底层的 Chrome 成因。
另请参阅
标题为“另请参阅”的章节- 安装指南:/integrations/artisan/install/
- 配置:/integrations/artisan/configuration/
- 安全与运维:/integrations/artisan/security-and-operations/
- Chrome 渲染器设置:/integrations/artisan/chrome-renderer-setup/
- 生产环境使用:/integrations/artisan/production-usage/