Bỏ qua để đến nội dung

Artisan trong production

Trong môi trường production, hãy inject một renderer đã được cấu hình cùng một logger PHP Standards Recommendation 3 (PSR-3), tái sử dụng tiến trình Chrome đang chạy giữa các lần kết xuất, đặt chiều cao tường minh cho tài liệu có nhiều phần tử và giới hạn đường kết xuất bằng một timeout ở lớp trên.

BrowserPool duy trì một tiến trình Chrome luôn chạy (keepAlive: true) và khởi động lại tiến trình đó sau mỗi 100 lần kết xuất để hạn chế mức tăng bộ nhớ, một dạng tích lũy đã biết ở các client Chrome DevTools Protocol (CDP) chạy lâu dài. Với một worker kết xuất nhiều tài liệu, hãy dùng một renderer sống lâu thay vì tạo một renderer cho từng yêu cầu, để hiếm khi phải trả chi phí khởi động Chrome.

render-service.php
<?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();
}
}

Hãy khởi tạo renderer một lần, rồi tái sử dụng nó. Gọi close() khi worker tắt để giải phóng tiến trình Chrome một cách xác định thay vì chờ destructor. Hai nhánh catch tách lỗi triển khai (thiếu runtime) khỏi lỗi trong lúc kết xuất (có thể thử lại). Đừng dùng khối catch rỗng.

Hãy gắn nó vào container dưới dạng singleton:

$container->singleton(ReportRenderer::class, fn ($c) =>
new ReportRenderer($c->get(Psr\Log\LoggerInterface::class)));

Khi bạn bỏ qua chiều cao, cầu nối sẽ đo chiều cao nội dung trong Chrome (max của các chiều cao scroll và offset của body/document), chuyển sang điểm và cộng thêm một vùng đệm an toàn khoảng 0,2 inch (khoảng 14,4 pt). Vùng đệm này bù cho độ lệch giữa bố cục viewport của Chrome và quá trình reflow bố cục in. Nếu không có nó, printToPDF có thể đẩy nội dung tràn sang trang thứ hai, rồi bị PageImporter (chỉ trang 0) cắt bỏ. Cầu nối áp đặt chiều cao giấy tối thiểu là 0,1 inch. Các test ChromeHtmlRendererTest::renderUsesAutoFitHeightByDefault, ::renderAutoFitBufferIsAddedNotSubtracted::renderAppliesMinimumHeightOf0Point1InchForTinyExplicitHeight kiểm chứng hành vi này.

Với tài liệu có bố cục cố định (hóa đơn, chứng chỉ), hãy truyền chiều cao tường minh theo điểm. Khi chiều cao đã tường minh, không có vùng đệm nào được thêm vào, và đầu ra khớp chính xác với khổ giấy được yêu cầu (được kiểm chứng bởi ::renderHonorsExplicitHeightWithoutAutoBuffer).

  • Hãy khởi tạo một renderer cho mỗi worker và tái sử dụng nó. BrowserPool tái sử dụng trình duyệt đang chạy và tự động khởi động lại ở mốc 100 lần kết xuất.
  • Hãy gọi close() khi worker tắt và giữa các batch lớn khi bạn muốn có một tiến trình Chrome mới sớm hơn mốc 100 lần kết xuất.
  • Destructor gọi close(), nhưng lời gọi close() tường minh có tính xác định hơn và được ưu tiên trong các tiến trình chạy lâu dài.
  • Thông báo khởi động lại được ghi log ở mức notice kèm số lần kết xuất; hãy cảnh báo khi tỷ lệ khởi động lại tăng cao vì điều đó cho thấy tài liệu nặng hơn dự kiến.

Hãy inject một logger PSR-3. Renderer phát ra các sự kiện và mức log sau:

Sự kiệnMứcNgữ cảnh
Bắt đầu kết xuấtdebugsize, width, height
Kết xuất hoàn tấtdebugpdfSize, contentHeight
Khởi chạy trình duyệtinfobinary
Khởi động lại trình duyệtnoticecount
Đóng trình duyệtdebugrenderCount

Không có HTML, byte PDF hay văn bản đã trích xuất nào được ghi log. Điều này giữ payload nằm ngoài log vận hành và phù hợp với hướng dẫn về nội dung log của National Institute of Standards and Technology Special Publication (NIST SP) 800-92. Hãy xây dựng các mục tiêu mức dịch vụ (SLO) về độ trễ từ cặp start/complete, và tạo cảnh báo về tỷ lệ khởi động lại dựa trên các sự kiện notice.

  • Sidecar Chrome: chạy Chrome trong cùng container với PHP worker; ghim chrome_binary. Hãy cấp phát một container hỗ trợ sandbox; xem /integrations/artisan/chrome-renderer-setup/.
  • Không container / CLI: Artisan không có container dependency injection. Hãy dùng EInvoiceServiceFactory cho các hợp đồng e-invoice Premium trong các runner giao diện dòng lệnh (CLI); xem /integrations/artisan/boot-and-discovery/.
  • Giới hạn tài nguyên: hãy ghép render_timeout với ngân sách yêu cầu ở lớp trên và cgroup/ulimit của host. Xem mô hình mối đe dọa tại /integrations/artisan/security-and-operations/.

Các trường hợp biên & điều cần lưu ý

Phần tiêu đề “Các trường hợp biên & điều cần lưu ý”
  • Một renderer gặp lỗi giữa lúc kết xuất vẫn đóng trang Chrome (finally), và pool vẫn dùng được.
  • Không hỗ trợ tái sử dụng một renderer trên nhiều threads/processes; một renderer sở hữu một tiến trình Chrome.
  • Mốc khởi động lại sau 100 lần kết xuất là cố định; hãy tính kích thước batch dựa trên mốc đó để các đợt tăng độ trễ vẫn dự đoán được.

Chi phí ở trạng thái ổn định đến từ phần bố cục đầu vào của Chrome cộng với printToPDF, chứ không phải overhead của cầu nối. keepAlive phân bổ chi phí khởi động trải đều qua các lần kết xuất. Hãy lường trước một đợt tăng độ trễ ở mỗi lần kết xuất thứ 100 (khởi động lại tiến trình); hãy đưa nó vào các SLO thay vì xem đó là sự cố.

Các đường dẫn production nhận HTML không tin cậy. Hãy đọc lại /integrations/artisan/security-and-operations/. Các rào cản mạng vẫn giữ nguyên bất kể cấu hình, nhưng no_sandbox: true loại bỏ sự cô lập tiến trình Chrome và nâng yêu cầu về độ tin cậy của đầu vào.

Trong các worker không container, EInvoiceServiceFactory trả về null khi Premium chưa được cài đặt, nên đường kết xuất nguồn mở tiếp tục không thay đổi. Hãy cài đặt Pro/Enterprise để bật tính năng nhúng và xác thực e-invoice trên tài liệu đã kết xuất.

  • /integrations/artisan/quickstart/
  • /integrations/artisan/configuration/
  • /integrations/artisan/security-and-operations/
  • /integrations/artisan/chrome-renderer-setup/
  • /integrations/artisan/troubleshooting/