在正式環境中使用 Artisan
重點摘要
標題為「重點摘要」的區段在正式環境中,請注入已設定好的 renderer(渲染器)與 PSR-3 logger,讓同一個執行中的 Chrome 行程能跨多次繪製重複使用;為包含多個元素的文件提供明確高度,並在進入繪製路徑之前,用上游 timeout(逾時)設定界限。
概念說明
標題為「概念說明」的區段BrowserPool 讓單一 Chrome 行程持續存活(keepAlive: true),並在每繪製 100 次後重啟一次,以限制記憶體成長——這是長壽命 CDP client 的已知累積模式。對於需要繪製大量文件的 worker,你會需要一個長壽命的 renderer,而不是每個請求各自建立一個,如此才能將 Chrome 的啟動成本攤到最低。
程式碼範例——正式環境
標題為「程式碼範例——正式環境」的區段<?php
declare(strict_types=1);
use NextPDF\Artisan\ChromeHtmlRenderer;use NextPDF\Artisan\ChromeRendererConfig;use NextPDF\Artisan\Exception\ChromeNotAvailableException;use NextPDF\Artisan\Exception\ChromeRenderException;use Psr\Log\LoggerInterface;
final class ReportRenderer{ private ChromeHtmlRenderer $renderer;
public function __construct(LoggerInterface $logger) { $config = ChromeRendererConfig::fromArray([ 'chrome_binary' => getenv('CHROME_BINARY') ?: null, 'render_timeout' => 45, 'max_html_size' => 2_000_000, 'no_sandbox' => (bool) getenv('CHROME_NO_SANDBOX'), ]);
$this->renderer = new ChromeHtmlRenderer($config, $logger); }
public function render(string $html, float $widthPt, float $heightPt = 0.0): string { try { return $this->renderer->render($html, $widthPt, $heightPt)->getPdfData(); } catch (ChromeNotAvailableException $e) { // Deployment fault: Chrome runtime missing. Page the on-call owner. throw $e; } catch (ChromeRenderException $e) { // Render-time fault: timeout, crash, empty output. Retryable once. throw $e; } }
public function shutdown(): void { $this->renderer->close(); }}renderer 只需建構一次,之後即可重複使用。在 worker 關閉時呼叫 close(),能確定地釋放 Chrome 行程,而不必等待解構子(destructor)。這兩個 catch 分支會區分部署層面的錯誤(執行環境缺失)與繪製階段的錯誤(可重試)。不要使用空的 catch 區塊。
以單例(singleton)方式將它接入容器:
$container->singleton(ReportRenderer::class, fn ($c) => new ReportRenderer($c->get(Psr\Log\LoggerInterface::class)));高度處理
標題為「高度處理」的區段省略高度時,橋接會在 Chrome 中量測內容高度(取 body/document 捲動高度與位移高度的 max),轉換為點,並加上約 0.2 英吋(約 14.4 pt)的安全緩衝。這個緩衝用來吸收 Chrome 視區版面與列印版面回流之間的差異。若沒有它,printToPDF 可能溢出到第二頁,而 PageImporter(僅取第 0 頁)會把第二頁裁切掉。系統會強制套用 0.1 英吋的最小紙張高度。ChromeHtmlRendererTest::renderUsesAutoFitHeightByDefault、::renderAutoFitBufferIsAddedNotSubtracted 與 ::renderAppliesMinimumHeightOf0Point1InchForTinyExplicitHeight 這幾個測試會驗證此行為。
對於固定版面的文件(發票、憑證),請以點為單位傳入明確高度。高度明確指定時,不會加上緩衝;輸出會精準符合要求的紙張尺寸(由 ::renderHonorsExplicitHeightWithoutAutoBuffer 驗證)。
批次 worker
標題為「批次 worker」的區段- 每個 worker 都建構一個 renderer,並重複使用。
BrowserPool會重複使用執行中的瀏覽器,並在每 100 次繪製的界線上自動重啟。 - 若你想在 100 次繪製的界線之前取得全新的 Chrome 行程,請在 worker 關閉時以及在大型批次之間呼叫
close()。 - 解構子會呼叫
close(),但明確呼叫close()更具確定性,是長時間執行行程中的首選做法。 - 重啟通知會以
notice等級連同繪製次數一併記錄;重啟頻率升高時請發出警示,這代表文件比預期更重。
可觀測性
標題為「可觀測性」的區段注入 PSR-3 logger。發出的事件與等級如下:
| 事件 | 等級 | 情境資料 |
|---|---|---|
| 繪製開始 | debug | size, width, height |
| 繪製完成 | debug | pdfSize, contentHeight |
| 瀏覽器啟動 | info | binary |
| 瀏覽器重啟 | notice | count |
| 瀏覽器關閉 | debug | renderCount |
不會記錄任何 HTML、PDF 位元組或擷取出的文字;這與 NIST SP 800-92 關於避免將酬載寫入維運日誌的指引一致。請用 start/complete 這組事件建立延遲 SLO,並用 notice 事件建立重啟頻率警示。
部署模式
標題為「部署模式」的區段- Sidecar Chrome:在與 PHP worker 相同的容器中執行 Chrome,並釘選
chrome_binary。佈建支援 sandbox 的容器;請參閱 /integrations/artisan/chrome-renderer-setup/。 - 無容器/CLI:Artisan 沒有 DI 容器。在 CLI runner 中,請使用
EInvoiceServiceFactory來建立 Premium 電子發票合約;請參閱 /integrations/artisan/boot-and-discovery/。 - 資源界限:將
render_timeout與上游請求預算,以及主機端的 cgroup/ulimit 搭配使用。請參閱 /integrations/artisan/security-and-operations/ 的威脅模型。
邊界情況與陷阱
標題為「邊界情況與陷阱」的區段- 若 renderer 在繪製途中被中斷,仍會關閉該 Chrome 頁面(
finally),pool 仍可繼續使用。 - 不支援跨 threads/processes 重複使用同一個 renderer;一個 renderer 擁有一個 Chrome 行程。
- 100 次繪製後重啟這個門檻是固定的;安排批次大小時請將它納入考量,以取得可預期的延遲尖峰。
穩定狀態下的成本來自 Chrome 對輸入進行版面配置再加上 printToPDF,而不是橋接本身的額外開銷。啟動成本由 keepAlive 攤平。預期每第 100 次繪製(行程重啟)都會出現一次延遲尖峰;請把它呈現在 SLO 中,而不是當成事件處理。
安全性說明
標題為「安全性說明」的區段正式環境路徑正是不可信任 HTML 的進入點。請重新詳閱 /integrations/artisan/security-and-operations/。不論如何設定,網路層屏障都仍然成立,但 no_sandbox: true 會移除 Chrome 的行程隔離,並提高對輸入的信任要求。
商業情境
標題為「商業情境」的區段在無容器的 worker 中,未安裝 Premium 時 EInvoiceServiceFactory 會回傳 null,因此開源的繪製路徑會照常運作;安裝 Pro/Enterprise 即可在已繪製的文件上啟用電子發票嵌入與驗證。
另請參閱
標題為「另請參閱」的區段- 參閱 /integrations/artisan/quickstart/ 一節
- 參閱 /integrations/artisan/configuration/ 一節
- 參閱 /integrations/artisan/security-and-operations/ 一節
- 參閱 /integrations/artisan/chrome-renderer-setup/ 一節
- 參閱 /integrations/artisan/troubleshooting/ 一節