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

Sử dụng NextPDF Symfony trong môi trường production

Hãy dùng bundle này trong các runtime PHP chạy dài hạn. Bundle tạo các tài liệu không dùng chung, khóa font registry sau khi warmup và đặt lại bộ nhớ đệm hình ảnh giữa các yêu cầu. Hãy truyền luồng các tệp Portable Document Format (PDF) lớn và chuyển tác vụ nặng sang Messenger worker xử lý.

Các runtime chạy dài hạn giữ container tồn tại qua nhiều yêu cầu, vì vậy trạng thái của từng yêu cầu phải được cô lập. FrankenPHP, RoadRunner và Messenger worker đều theo mô hình này. Tệp services.php của bundle định nghĩa các vòng đời bên dưới, đã được kiểm chứng dựa trên định nghĩa service:

  • Document — không dùng chung. nextpdf.document (cùng các alias PdfDocumentInterface / Document) trả về một instance mới cho mỗi lần phân giải. Theo PSR-11 (PHP Standard Recommendation 11), container hoàn toàn có thể trả về giá trị khác nhau ở mỗi lần get() cho cùng một id (PSR-11 §1.1.2). Hãy phân giải một tài liệu cho mỗi yêu cầu. Đừng bao giờ giữ lại tài liệu đó qua nhiều yêu cầu.
  • FontRegistry — dùng chung và bị khóa. Registry này là một singleton trong suốt vòng đời tiến trình. Sau warmup() (khi preload_fonts không rỗng), compiler pass sẽ gọi lock(). Việc khóa ngăn thay đổi trong lúc chạy và tránh làm nhiễm trạng thái phông chữ giữa các yêu cầu.
  • ImageRegistry — dùng chung, đặt lại theo mỗi yêu cầu. Bộ nhớ đệm hình ảnh có giới hạn theo cơ chế least recently used (LRU) được dùng chung, nhưng được gắn thẻ kernel.reset với phương thức reset, nên Symfony sẽ xóa bộ nhớ đệm này giữa các yêu cầu trên những runtime tôn trọng kernel.reset.
  • Các contract EInvoice — không dùng chung. Khi có các phần triển khai Premium, các service embedder, validator, profile và schematron được đăng ký dưới dạng không dùng chung. Ngữ cảnh parser luôn nằm trong phạm vi từng lần gọi và không bao giờ rò rỉ qua các yêu cầu.

Hãy inject PdfFactory, nơi giữ cấu hình dùng chung và không trạng thái, rồi gọi create() cho mỗi yêu cầu:

public function __construct(private readonly PdfFactory $pdf) {}
public function action(): Response
{
$doc = $this->pdf->create(); // fresh, disposable
// ... build ...
return PdfResponse::inline($doc, 'document.pdf');
}

Đừng inject Document hoặc nextpdf.document vào một service dùng chung được giữ lại qua nhiều yêu cầu. Thay vào đó, hãy phân giải tài liệu bên trong phương thức có phạm vi theo yêu cầu.

PdfResponse::streamDownload()streamInline() trả về một StreamedResponse. Callback phát phần thân PDF theo từng khối 64 KB và xả bộ đệm sau mỗi khối, nhờ đó giới hạn bộ đệm phản hồi đối với các tài liệu lớn. Các hành vi bên dưới đã được kiểm chứng dựa trên PdfResponse:

  • Các biến thể truyền luồng cố ý bỏ qua Content-Length vì đối tượng phản hồi không biết trước kích thước phần thân. Thanh tiến trình tải xuống và một số proxy hoạt động tốt hơn khi biết trước độ dài. Hãy dùng download() hoặc inline() không truyền luồng khi tài liệu đủ nhỏ để giữ trong bộ nhớ và bạn cần content length.
  • Các biến thể truyền luồng phát ra cùng các header bảo mật và cùng Cache-Control: private, max-age=0, must-revalidate như những biến thể có đệm.

Hãy chọn truyền luồng cho các báo cáo nhiều megabyte và các đợt xuất hàng loạt. Hãy chọn biến thể có đệm cho những phản hồi nhỏ, nhạy với độ trễ.

Hãy chuyển việc tạo tài liệu sang Messenger khi yêu cầu phải trả về nhanh, hoặc khi việc kết xuất tiêu tốn nhiều tài nguyên xử lý.

  1. Triển khai PdfBuilderInterface cho mỗi loại tài liệu.
  2. Đăng ký các builder trong một container.service_locator và kết nối service locator đó vào GeneratePdfHandler, qua tham số $builderLocator của handler.
  3. Định tuyến GeneratePdfMessage đến một transport bền vững.
  4. Chạy các worker với vòng đời có giới hạn.

Hãy tái tạo lại các worker để một vùng cấp phát bị rò rỉ trong dependency của bên thứ ba không thể phình ra vô hạn:

Terminal window
php bin/console messenger:consume async \
--limit=200 \
--memory-limit=256M \
--time-limit=3600

Các khóa cấu hình messenger.timeoutmessenger.retries của bundle ghi lại thời gian chờ và ngân sách thử lại dự kiến cho mỗi message. Hãy áp dụng cùng hành vi đó thông qua chiến lược thử lại của Symfony và các cờ của worker.

GeneratePdfMessage xác thực đường dẫn đầu ra tại thời điểm khởi tạo. GeneratePdfHandler xác thực lại đường dẫn đó tại thời điểm thực thi, trước khi ghi xuống đĩa. Việc kiểm tra hai giai đoạn này rất quan trọng đối với công việc bất đồng bộ. Một message có thể nằm trong hàng đợi từ lúc gửi đi đến lúc được tiêu thụ, nên handler không tin tưởng mù quáng vào đường dẫn đã được xếp hàng. Hãy giới hạn quyền truy cập hệ thống tệp của worker chỉ trong thư mục đầu ra dự kiến như một lớp phòng thủ theo chiều sâu.

Các service FontRegistryImageRegistry chấp nhận một Psr\Log\LoggerInterface tùy chọn (được gắn bằng nullOnInvalid()). Khi ứng dụng cung cấp một logger, các registry có thể phát thông tin chẩn đoán qua logger đó. Logger là một collaborator tùy chọn, có thể thay thế, theo contract logger PSR-3 (PSR-3). Để quan sát ở cấp độ yêu cầu, hãy ghi log quanh PdfFactory::create() và Messenger handler trong mã ứng dụng của bạn. Hãy dùng messenger:consume -vv khi phân loại sự cố.

  • Ghim một phiên bản major của nextpdf/core trong composer.json của ứng dụng (bundle chấp nhận ^3.0 || ^5.2).
  • Đảm bảo ext-mbstringext-zlib được bật trong PHP image đã triển khai (nếu không, bundle sẽ báo lỗi ngay khi khởi động).
  • Điền sẵn vào preload_fonts các phông chữ mà tài liệu của bạn sử dụng để registry được warmup và khóa ngay khi khởi động thay vì ở yêu cầu đầu tiên.
  • Trỏ cache_path đến một vị trí có thể ghi và bền vững nếu bạn dựa vào các artifact đã được lưu đệm qua các lần triển khai. Nếu không, giá trị mặc định %kernel.cache_dir% là ổn.
  • Chạy php bin/console cache:warmup trong quá trình triển khai để container đã biên dịch (gồm cả các phép dò extension tùy chọn) được dựng trước khi có lưu lượng truy cập.
  • Dùng một transport Messenger bền vững (không phải sync) cho công việc bất đồng bộ trong môi trường production, và tái tạo lại các worker bằng --limit / --memory-limit / --time-limit.
  • Phản hồi truyền luồng phía sau một proxy có đệm — Một proxy đệm toàn bộ phần thân sẽ làm mất lợi ích về bộ nhớ. Hãy cấu hình proxy để truyền luồng các phản hồi PDF, hoặc dùng phản hồi có đệm tại đó.
  • kernel.reset không được tôn trọng — Trên một runtime không gọi kernel.reset, bộ nhớ đệm hình ảnh vẫn bị giới hạn bởi image_cache_mb nhưng không được xóa giữa các yêu cầu; hãy đặt giới hạn cho phù hợp.
  • Giữ một tài liệu qua nhiều yêu cầu — Một Document được giữ lại từ yêu cầu trước đó sẽ mang theo trạng thái cũ. Hãy luôn phân giải theo từng yêu cầu thông qua PdfFactory.

Mỗi hàng là một tuyên bố chuẩn tắc trên trang này, được ghim vào một reference_id 64 ký tự hex đầy đủ từ kho ngữ liệu standards development organization (SDO) có kiểm soát. Nguồn gốc của manifest kho ngữ liệu và transport truy xuất nằm trong _sidecars/rag-citations.yaml.

Đặc tảĐiều khoảnreference_idTuyên bố
PSR-11psr_11_container#1.1.2.p3.bService không dùng chung: giá trị khác nhau cho mỗi lần phân giải
PSR-3psr_3_logger#x3.p17Collaborator logger tùy chọn
  • /integrations/symfony/configuration/ — vòng đời service và tham số.
  • /integrations/symfony/security-and-operations/ — header phản hồi, xác thực đường dẫn, xử lý khóa.
  • /integrations/symfony/troubleshooting/ — chẩn đoán khi khởi động và trong lúc chạy.
  • /integrations/symfony/quickstart/ — thiết lập bất đồng bộ tối thiểu.