Sử dụng NextPDF Symfony trong môi trường production
Tổng quan nhanh
Phần tiêu đề “Tổng quan nhanh”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ý.
Vòng đời service an toàn cho worker
Phần tiêu đề “Vòng đời service an toàn cho worker”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 aliasPdfDocumentInterface/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ầnget()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()(khipreload_fontskhông rỗng), compiler pass sẽ gọilock(). 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.resetvới phương thứcreset, 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ọngkernel.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.
Mẫu injection được khuyến nghị
Phần tiêu đề “Mẫu injection được khuyến nghị”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.
Truyền luồng các tài liệu lớn
Phần tiêu đề “Truyền luồng các tài liệu lớn”PdfResponse::streamDownload() và 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-Lengthvì đố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ùngdownload()hoặcinline()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-revalidatenhư 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ễ.
Tạo bất đồng bộ ở quy mô lớn
Phần tiêu đề “Tạo bất đồng bộ ở quy mô lớn”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ý.
- Triển khai
PdfBuilderInterfacecho mỗi loại tài liệu. - Đăng ký các builder trong một
container.service_locatorvà kết nối service locator đó vàoGeneratePdfHandler, qua tham số$builderLocatorcủa handler. - Định tuyến
GeneratePdfMessageđến một transport bền vững. - Chạy các worker với vòng đời có giới hạn.
Vòng đời worker có giới hạn
Phần tiêu đề “Vòng đời worker 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:
php bin/console messenger:consume async \ --limit=200 \ --memory-limit=256M \ --time-limit=3600Các khóa cấu hình messenger.timeout và messenger.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.
An toàn đường dẫn đầu ra trong worker
Phần tiêu đề “An toàn đường dẫn đầu ra trong 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.
Khả năng quan sát
Phần tiêu đề “Khả năng quan sát”Các service FontRegistry và ImageRegistry 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ố.
Danh sách kiểm tra triển khai
Phần tiêu đề “Danh sách kiểm tra triển khai”- Ghim một phiên bản major của
nextpdf/coretrongcomposer.jsoncủa ứng dụng (bundle chấp nhận^3.0 || ^5.2). - Đảm bảo
ext-mbstringvàext-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_fontscá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:warmuptrong 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.
Trường hợp đặc biệt và lưu ý
Phần tiêu đề “Trường hợp đặc biệt và lưu ý”- 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.resetkhông được tôn trọng — Trên một runtime không gọikernel.reset, bộ nhớ đệm hình ảnh vẫn bị giới hạn bởiimage_cache_mbnhư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 quaPdfFactory.
Tuân thủ
Phần tiêu đề “Tuân thủ”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ản | reference_id | Tuyên bố |
|---|---|---|---|
| PSR-11 | psr_11_container#1.1.2.p3.b | Service không dùng chung: giá trị khác nhau cho mỗi lần phân giải | |
| PSR-3 | psr_3_logger#x3.p17 | Collaborator logger tùy chọn |
Xem thêm
Phần tiêu đề “Xem thêm”- /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.