Artisan в продакшене
В продакшене внедряйте настроенный модуль отрисовки и логгер PHP Standards Recommendation 3 (PSR-3), повторно используйте запущенный процесс Chrome между отрисовками, задавайте явную высоту для многоэлементных документов и ограничивайте путь отрисовки внешним тайм-аутом выше по цепочке.
Общая концепция
Заголовок раздела «Общая концепция»BrowserPool поддерживает один запущенный процесс Chrome (keepAlive: true) и перезапускает его каждые 100 отрисовок, чтобы ограничить рост потребления памяти — известный сценарий накопления у долгоживущих клиентов Chrome DevTools Protocol (CDP). Для обработчика, который отрисовывает много документов, используйте один долгоживущий модуль отрисовки вместо отдельного модуля на каждый запрос — так затраты на запуск 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(); }}Создайте модуль отрисовки один раз, затем используйте его повторно. Вызывайте close() при завершении работы обработчика, чтобы детерминированно освободить процесс Chrome, а не ждать деструктора. Две ветви catch разделяют сбой развёртывания (отсутствует среда выполнения) и сбой во время отрисовки (при котором возможен повтор). Не используйте пустые блоки catch.
Подключите его в контейнер как синглтон:
$container->singleton(ReportRenderer::class, fn ($c) => new ReportRenderer($c->get(Psr\Log\LoggerInterface::class)));Обработка высоты
Заголовок раздела «Обработка высоты»Если вы не задаёте высоту, мост измеряет высоту содержимого в Chrome (max из высот прокрутки и смещения body/document), преобразует её в пункты и добавляет страховочный запас ~0.2 дюйма (~14.4 pt). Этот запас покрывает расхождение между макетом области просмотра Chrome и перекомпоновкой под печатный макет. Без него printToPDF может перенести содержимое на вторую страницу, которую PageImporter (только страница 0) обрежет. Мост задаёт минимальную высоту листа в 0.1 дюйма. Это поведение проверяют тесты ChromeHtmlRendererTest::renderUsesAutoFitHeightByDefault, ::renderAutoFitBufferIsAddedNotSubtracted и ::renderAppliesMinimumHeightOf0Point1InchForTinyExplicitHeight.
Для документов с фиксированным макетом (счета, сертификаты) передавайте явную высоту в пунктах. Когда высота задана явно, запас не добавляется, а результат точно соответствует запрошенному размеру листа (проверяется тестом ::renderHonorsExplicitHeightWithoutAutoBuffer).
Пакетные обработчики
Заголовок раздела «Пакетные обработчики»- Создавайте один модуль отрисовки на обработчик и используйте его повторно.
BrowserPoolповторно использует запущенный браузер и автоматически перезапускает его на границе 100 отрисовок. - Вызывайте
close()при завершении работы обработчика и между крупными пакетами, когда вам нужен свежий процесс Chrome раньше, чем наступит граница 100 отрисовок. - Деструктор вызывает
close(), но явный вызовclose()детерминирован и предпочтителен в длительно работающих процессах. - Уведомления о перезапуске записываются на уровне
noticeвместе со счётчиком отрисовок; настройте оповещение при повышенной частоте перезапусков, потому что это указывает на документы тяжелее ожидаемого.
Наблюдаемость
Заголовок раздела «Наблюдаемость»Внедрите логгер PSR-3. Модуль отрисовки генерирует следующие события и уровни:
| Событие | Уровень | Контекст |
|---|---|---|
| Начало отрисовки | debug | size, width, height |
| Отрисовка завершена | debug | pdfSize, contentHeight |
| Запуск браузера | info | binary |
| Перезапуск браузера | notice | count |
| Закрытие браузера | debug | renderCount |
Ни HTML, ни байты PDF, ни извлечённый текст в журнал не записываются. Так полезная нагрузка не попадает в операционные журналы, что соответствует рекомендациям National Institute of Standards and Technology Special Publication (NIST SP) 800-92 по содержимому журналов. Стройте цели уровня обслуживания (SLO) по задержке на основе пары start/complete, а оповещение о частоте перезапусков — на основе событий notice.
Схемы развёртывания
Заголовок раздела «Схемы развёртывания»- Sidecar-контейнер с Chrome: запускайте Chrome в том же контейнере, что и обработчик PHP; зафиксируйте
chrome_binary. Подготовьте контейнер с поддержкой песочницы; см. /integrations/artisan/chrome-renderer-setup/. - Без контейнера / CLI: в Artisan нет контейнера внедрения зависимостей. Используйте
EInvoiceServiceFactoryдля контрактов электронных счетов Premium в исполнителях командной строки (CLI); см. /integrations/artisan/boot-and-discovery/. - Ограничение ресурсов: сочетайте
render_timeoutс бюджетом запроса выше по цепочке и хостовыми cgroup/ulimit. См. модель угроз на /integrations/artisan/security-and-operations/.
Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»- Модуль отрисовки, прерванный в середине отрисовки, всё равно закрывает страницу Chrome (
finally), поэтому пул остаётся пригодным к использованию. - Повторное использование одного модуля отрисовки между threads/processes не поддерживается; один модуль отрисовки владеет одним процессом Chrome.
- Перезапуск через каждые 100 отрисовок фиксирован; рассчитывайте размер пакетов с учётом этого, чтобы всплески задержки оставались предсказуемыми.
Производительность
Заголовок раздела «Производительность»Затраты в установившемся режиме — это компоновка входных данных в Chrome плюс printToPDF, а не накладные расходы моста. keepAlive распределяет затраты на запуск по нескольким отрисовкам. Ожидайте всплеск задержки на каждой 100-й отрисовке (перезапуск процесса); отражайте его в SLO, а не рассматривайте как инцидент.
Заметки по безопасности
Заголовок раздела «Заметки по безопасности»В продакшене на вход поступает недоверенный HTML. Перечитайте /integrations/artisan/security-and-operations/. Сетевые барьеры действуют независимо от конфигурации, но no_sandbox: true отключает изоляцию процесса Chrome и повышает требования к доверию к входным данным.
Коммерческий контекст
Заголовок раздела «Коммерческий контекст»В обработчиках без контейнера EInvoiceServiceFactory возвращает null, когда Premium не установлен, поэтому путь отрисовки с открытым исходным кодом продолжает работать без изменений. Установите Pro/Enterprise, чтобы включить встраивание и проверку электронных счетов в отрисованном документе.
См. также
Заголовок раздела «См. также»- Быстрый старт — /integrations/artisan/quickstart/
- Настройка моста Chrome — /integrations/artisan/configuration/
- Безопасность и эксплуатация — /integrations/artisan/security-and-operations/
- Настройка средства отрисовки Chrome — /integrations/artisan/chrome-renderer-setup/
- Устранение неполадок — /integrations/artisan/troubleshooting/