NextPDF Symfony в продакшене
Используйте пакет в долгоживущих средах выполнения PHP. Он создаёт необщие документы, блокирует реестр шрифтов после прогрева и сбрасывает кэш изображений между запросами. Передавайте большие файлы Portable Document Format (PDF) потоком, а тяжёлые задачи переносите на воркеры Messenger.
Жизненный цикл служб, безопасный для воркеров
Заголовок раздела «Жизненный цикл служб, безопасный для воркеров»Долгоживущие среды выполнения сохраняют контейнер между запросами, поэтому состояние каждого запроса должно оставаться изолированным. FrankenPHP, RoadRunner и воркеры Messenger следуют этой модели. В файле services.php пакет задаёт следующий жизненный цикл, сверенный с определениями служб:
- Document — необщий.
nextpdf.document(и псевдонимыPdfDocumentInterface/Document) при каждом разрешении возвращают новый экземпляр. Согласно PSR-11 (PHP Standard Recommendation 11), контейнер может возвращать разное значение при каждом вызовеget()для одного и того же идентификатора (PSR-11 §1.1.2). Разрешайте документ для каждого запроса. Никогда не удерживайте его между запросами. - FontRegistry — общий и заблокированный. Реестр существует в одном экземпляре на всё время жизни процесса. После
warmup()(когдаpreload_fontsне пуст) проход компилятора вызываетlock(). Блокировка не даёт менять реестр во время выполнения и загрязнять состояние шрифтов между запросами. - ImageRegistry — общий, сбрасывается при каждом запросе. Ограниченный кэш изображений с вытеснением наименее недавно использованных элементов (LRU) является общим, но помечен тегом
kernel.resetс методомreset, поэтому Symfony очищает его между запросами в средах выполнения, которые учитываютkernel.reset. - Контракты EInvoice — необщие. Когда доступны реализации Premium, службы встраивания, валидатора, профиля и schematron регистрируются как необщие. Контекст парсера остаётся ограниченным текущим вызовом и никогда не утекает между запросами.
Рекомендуемый шаблон внедрения
Заголовок раздела «Рекомендуемый шаблон внедрения»Внедрите PdfFactory — общий держатель конфигурации без состояния — и вызывайте create() для каждого запроса:
public function __construct(private readonly PdfFactory $pdf) {}
public function action(): Response{ $doc = $this->pdf->create(); // fresh, disposable // ... build ... return PdfResponse::inline($doc, 'document.pdf');}Не внедряйте Document или nextpdf.document в общую службу, которая удерживается между запросами. Вместо этого разрешайте его в методе, работающем в области видимости запроса.
Потоковая передача больших документов
Заголовок раздела «Потоковая передача больших документов»PdfResponse::streamDownload() и streamInline() возвращают StreamedResponse. Обратный вызов выдаёт тело PDF блоками по 64 KB и сбрасывает буфер после каждого блока, что ограничивает размер буфера ответа для больших документов. Приведённое ниже поведение сверено с PdfResponse:
- Потоковые варианты намеренно опускают
Content-Length, потому что объект ответа заранее не знает размер тела. Индикаторы хода загрузки и некоторые прокси работают лучше, когда длина известна. Используйте непотоковыеdownload()илиinline(), когда документ достаточно мал, чтобы поместиться в памяти, и желательна длина содержимого. - Потоковые варианты выдают те же заголовки безопасности и тот же
Cache-Control: private, max-age=0, must-revalidate, что и буферизованные варианты.
Выбирайте потоковую передачу для многомегабайтных отчётов и пакетного экспорта. Для небольших ответов, чувствительных к задержке, выбирайте буферизованные варианты.
Асинхронная генерация при больших нагрузках
Заголовок раздела «Асинхронная генерация при больших нагрузках»Переносите генерацию на Messenger, когда ответ на запрос нужно вернуть быстро или когда рендеринг нагружает процессор.
- Реализуйте
PdfBuilderInterfaceдля каждого типа документа. - Зарегистрируйте построители в
container.service_locatorи передайте его вGeneratePdfHandlerкак$builderLocator. - Маршрутизируйте
GeneratePdfMessageв надёжный транспорт. - Запускайте воркеры с ограниченным временем жизни.
Ограниченное время жизни воркера
Заголовок раздела «Ограниченное время жизни воркера»Перезапускайте воркеры, чтобы утечка памяти в сторонней зависимости не росла без ограничений:
php bin/console messenger:consume async \ --limit=200 \ --memory-limit=256M \ --time-limit=3600Ключи конфигурации пакета messenger.timeout и messenger.retries задают предполагаемый тайм-аут на сообщение и бюджет повторных попыток. Обеспечьте такое же поведение через стратегию повторных попыток Symfony и флаги воркера.
Безопасность пути вывода в воркерах
Заголовок раздела «Безопасность пути вывода в воркерах»GeneratePdfMessage проверяет путь вывода при создании. GeneratePdfHandler повторно проверяет его во время выполнения, перед записью на диск. Эта двухэтапная проверка важна для асинхронной работы. Сообщение может находиться в очереди между отправкой и обработкой, поэтому обработчик не доверяет вслепую пути из очереди. Ограничьте права воркера в файловой системе предполагаемым каталогом вывода как дополнительный уровень защиты.
Наблюдаемость
Заголовок раздела «Наблюдаемость»Службы FontRegistry и ImageRegistry принимают необязательный Psr\Log\LoggerInterface (привязанный через nullOnInvalid()). Когда приложение предоставляет логгер, реестры могут писать через него диагностические сообщения. Логгер — необязательная заменяемая зависимость в рамках контракта логгера PSR-3 (PSR-3). Чтобы видеть происходящее на уровне запроса, ведите журналирование вокруг PdfFactory::create() и обработчика Messenger в коде вашего приложения. Используйте messenger:consume -vv при разборе инцидентов.
Контрольный список развёртывания
Заголовок раздела «Контрольный список развёртывания»- Зафиксируйте один мажорный выпуск
nextpdf/coreвcomposer.jsonприложения (пакет допускает^3.0 || ^5.2). - Убедитесь, что
ext-mbstringиext-zlibвключены в развёрнутом образе PHP (иначе загрузка пакета завершится аварийно). - Заранее укажите в
preload_fontsшрифты, которые используют ваши документы, чтобы реестр прогревался и блокировался при загрузке, а не при первом запросе. - Укажите
cache_pathна постоянное расположение с правом записи, если вы полагаетесь на кэшированные артефакты между развёртываниями. В противном случае значение по умолчанию%kernel.cache_dir%подходит. - Запускайте
php bin/console cache:warmupво время развёртывания, чтобы скомпилированный контейнер (включая проверки необязательных расширений) собирался до поступления трафика. - Используйте надёжный транспорт Messenger (не
sync) для асинхронной работы в продакшене и перезапускайте воркеры с помощью--limit/--memory-limit/--time-limit.
Граничные случаи и подводные камни
Заголовок раздела «Граничные случаи и подводные камни»- Потоковые ответы за буферизующим прокси — прокси, который буферизует всё тело, сводит на нет выигрыш по памяти. Настройте прокси на потоковую передачу ответов PDF либо используйте для таких маршрутов буферизованные ответы.
kernel.resetне учитывается — в среде выполнения, которая не вызываетkernel.reset, кэш изображений ограничен значениемimage_cache_mb, но не очищается между запросами; задавайте лимит соответственно.- Удержание документа между запросами — захваченный
Documentиз предыдущего запроса сохранит устаревшее состояние. Всегда разрешайте его для каждого запроса черезPdfFactory.
Соответствие требованиям
Заголовок раздела «Соответствие требованиям»Каждая строка содержит нормативное утверждение с этой страницы, привязанное к полному 64-символьному шестнадцатеричному reference_id из закрытого корпуса организации по разработке стандартов (SDO). Происхождение манифеста корпуса и транспорт извлечения указаны в _sidecars/rag-citations.yaml.
| Спецификация | Пункт | Идентификатор (reference_id) | Утверждение |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p3.b | Необщая служба: отдельное значение для каждого разрешения | |
| PSR-3 | psr_3_logger#x3.p17 | Необязательная зависимость логгера |
См. также
Заголовок раздела «См. также»- /integrations/symfony/configuration/ — жизненный цикл служб и параметры.
- /integrations/symfony/security-and-operations/ — заголовки ответа, проверка пути, работа с ключами.
- /integrations/symfony/troubleshooting/ — диагностика при загрузке и во время выполнения.
- /integrations/symfony/quickstart/ — минимальная асинхронная настройка.