Pro phiên bản
Stream
Tổng quan nhanh
Phần tiêu đề “Tổng quan nhanh”Module Stream kết xuất các batch tài liệu một cách bền bỉ và đồng thời, với commit cục bộ chính-xác-một-lần tới các kho bền bỉ đơn-host (chính-xác-một-lần liên-host là ranh giới của Enterprise Stream). Nó chia công việc thành hai trách nhiệm tách bạch sạch sẽ: một render engine biến các manifest đã được xác thực thành byte (và không gì khác), và một tập các kho bền bỉ — committer, checkpoint, idempotency, dead-letter — vốn xuất bản các byte đó một cách an toàn và để cho một lượt chạy có thể tiếp tục sau một sự cố mà không xuất bản lại đầu ra đã commit.
Tính khả dụng & cấp phép
Phần tiêu đề “Tính khả dụng & cấp phép”Khả năng này có trong NextPDF Pro (nextpdf/pro) và được kích hoạt bằng một phong bì giấy phép cấp Pro. Một triển khai không có quyền đó sẽ không nạp các lớp của khả năng này. So sánh các phiên bản và lấy giấy phép.
Không có cờ giấy phép riêng theo từng tính năng. Mức đồng thời (số lượng worker), kích thước batch, ngân sách thử lại, và backend kho (in-memory so với filesystem bền bỉ) là các tham số lúc chạy, không phải các công tắc giấy phép.
Cài đặt
Phần tiêu đề “Cài đặt”composer require nextpdf/pro:^3Mã nằm dưới namespace NextPDF\Pro\Stream.
Tổng quan khái niệm
Phần tiêu đề “Tổng quan khái niệm”Stream được tổ chức quanh một đường nối được đóng băng — NextPDF\Pro\Stream\Engine\RenderEngineInterface — vốn tách engine thông lượng khỏi ngữ nghĩa của luồng:
- Render engine sở hữu mức đồng thời và bộ nhớ có giới hạn. Nó kết xuất một cửa sổ các manifest đã được tiền-xác-thực, đã tiền-khử-trùng-lặp qua
renderBatch()và trả về mộtEngineRenderResultcho mỗi manifest, theo thứ tự đầu vào. Điều cốt yếu là engine không-tác-dụng-phụ đối với đầu ra cuối cùng: nó trả về các byte đã kết xuất cộng với digest sha-256 của chúng, không bao giờ ghi vào một object key cuối cùng. Chính sự tinh khiết đó làm cho việc giao chính-xác-một-lần trở nên khả thi. - Các cộng tác viên của luồng sở hữu việc giao. Committer, checkpoint store, idempotency (dedup) store, và dead-letter store quyết định byte đi về đâu, một lượt chạy tiếp tục thế nào, công việc nào là một lần phát lại, và điều gì xảy ra với các thất bại trạng-thái-cuối.
Một thất bại kết xuất theo từng manifest được báo cáo dưới dạng một kết quả Failed (hoặc Timeout) theo từng item; nó không bao giờ hủy bỏ batch. Phong bì batch luôn thành công với các kết quả theo từng item.
Khái niệm chính
Phần tiêu đề “Khái niệm chính”Render engine và executor
Phần tiêu đề “Render engine và executor”InProcessRenderEnginelà baseline đúng-đắn đồng bộ, đơn-tiến-trình. Nó xác thực mỗi manifest theo lối fail-closed quaRenderManifestValidatorđi kèm trước khi kết xuất nó quaSingleDocumentRenderercủa Core, nên một manifest hỏng trở thành một thất bại theo từng item (mã lỗiSPEC-MANIFEST-INVALID) thay vì chạm tới renderer.ConcurrentRenderEnginephân tán một batch ra mộtRenderUnitExecutorInterfacevà khôi phục thứ tự batch có tính tất định theo chỉ số unit. Đầu ra là byte-identical với một lượt kết xuất tuần tự bất kể thứ tự hoàn tất; một lần hoàn tất bị thiếu, trùng lặp, hay không xác định là một thất bại cứng, không bao giờ là một lần bỏ rơi âm thầm.- Executor là đường nối của mức đồng thời.
InlineRenderUnitExecutorlà baseline tất định;ProcessPoolRenderUnitExecutorphân phối một batch trên tối đa N tiến trình con workerphpvốn kết xuất song song, rồi thu thập và kiểm tra tính toàn vẹn cho các kết quả của chúng.
Commit bền bỉ, không-tác-dụng-phụ
Phần tiêu đề “Commit bền bỉ, không-tác-dụng-phụ”OutputCommitterInterface::commit() xuất bản các byte đã kết xuất tới đích cuối cùng của chúng đúng một lần: một cách nguyên tử (không bao giờ quan sát thấy một object dở dang), một cách idempotent (việc commit lại nội dung byte-identical không thực hiện ghi nào và trả về một CommitReceipt với idempotentReuse = true — một receipt mới, không phải bản gốc), không ghi đè âm thầm (các byte phân kỳ tới một key đã bị chiếm mà không có overwrite sẽ làm phát sinh một xung đột), và được kiểm tra toàn vẹn (committer tính lại digest trước khi ghi). LocalFilesystemCommitter hiện thực hóa điều này cho filesystem cục bộ.
Phục hồi checkpoint
Phần tiêu đề “Phục hồi checkpoint”Một RunCheckpoint là một rào cản bền bỉ ghi lại một lượt chạy đã commit bao nhiêu item cộng với một ảnh chụp của trạng thái có-khóa. Khi phục hồi, processor tua nhanh qua offset đã commit và khôi phục trạng thái có-khóa, nên một sự cố giữa lượt chạy sẽ tiếp tục mà không xuất bản lại đầu ra đã commit. FilesystemCheckpointStore lưu mỗi rào cản một cách nguyên tử.
Khử trùng lặp idempotency, thử lại, và dead-letter
Phần tiêu đề “Khử trùng lặp idempotency, thử lại, và dead-letter”Idempotency store là đường đi nhanh vốn để cho processor đoản mạch trước khi kết xuất một manifest đã phát lại; phép so sánh digest của committer vẫn là đảm bảo chính-xác-một-lần bền bỉ, nên một bản ghi dedup bị mất tệ nhất chỉ gây ra một lần kết xuất lại lãng phí mà committer sẽ khử trùng lặp. RetryPolicy cung cấp backoff hàm mũ có giới hạn, tất định cho các thất bại thoáng qua (timeout); một job làm cạn ngân sách của nó được ghi lại trong một DeadLetterStoreInterface thay vì bị mất. Mỗi kho đi kèm một biến thể in-memory (phạm vi đơn-lượt-chạy / test) và một biến thể filesystem bền bỉ.
Dấu hiệu bền bỉ
Phần tiêu đề “Dấu hiệu bền bỉ”Các kho có trạng thái sống sót qua một lần khởi động lại tiến trình sẽ hiện thực hóa dấu hiệu DurableCapability. Một lượt chạy an toàn trước sự cố đòi hỏi mọi cộng tác viên đều bền bỉ để nó thất bại nhanh thay vì hứa hẹn ngữ nghĩa chính-xác-một-lần mà một kho in-memory không thể giữ được qua một lần khởi động lại.
Ví dụ mã — Bắt đầu nhanh
Phần tiêu đề “Ví dụ mã — Bắt đầu nhanh”Kết xuất một manifest và commit các byte của nó đúng một lần. Engine trả về byte cộng với một digest; committer xuất bản chúng.
<?php
declare(strict_types=1);
use NextPDF\Manifest\OutputObjectKey;use NextPDF\Manifest\Render\SingleDocumentRenderer;use NextPDF\Manifest\RenderManifestBuilder;use NextPDF\Manifest\TemplateRef;use NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter;use NextPDF\Pro\Stream\Engine\InProcessRenderEngine;
$outputRoot = __DIR__ . '/out';\is_dir($outputRoot) || \mkdir($outputRoot, 0o775, true);
// The engine renders bytes only — it never writes the final object.$engine = new InProcessRenderEngine(SingleDocumentRenderer::standalone());
$target = OutputObjectKey::file('out', 'invoices/1001.pdf');
$manifest = RenderManifestBuilder::create('invoice-1001') ->withInlineInput('<h1>Invoice 1001</h1><p>Amount due: 42.00</p>') ->withTemplate(TemplateRef::html()) ->withOutputKey($target) ->build();
$result = $engine->renderBatch([$manifest])[0];
// A durable committer publishes the rendered bytes exactly once.$committer = new LocalFilesystemCommitter($outputRoot);
if ($result->isRendered()) { $receipt = $committer->commit($result->jobId, $target, $result->bytes, $result->sha256); echo $receipt->target->toUri(), ' (', $receipt->bytesWritten, " bytes)\n";}Ví dụ mã — Sản xuất
Phần tiêu đề “Ví dụ mã — Sản xuất”Kết xuất một batch, định tuyến các timeout tới retry policy, và đưa các thất bại trạng-thái-cuối vào dead-letter. Commit từ chối ghi đè các byte phân kỳ, nên một va chạm key được bắt và ghi lại thay vì bị mất.
<?php
declare(strict_types=1);
use DateTimeImmutable;use NextPDF\Manifest\OutputObjectKey;use NextPDF\Manifest\Render\SingleDocumentRenderer;use NextPDF\Manifest\RenderManifest;use NextPDF\Manifest\RenderManifestBuilder;use NextPDF\Manifest\TemplateRef;use NextPDF\Pro\Stream\Commit\LocalFilesystemCommitter;use NextPDF\Pro\Stream\Engine\EngineRenderStatus;use NextPDF\Pro\Stream\Engine\InProcessRenderEngine;use NextPDF\Pro\Stream\Exception\OutputCommitConflictException;use NextPDF\Pro\Stream\Retry\DeadLetterRecord;use NextPDF\Pro\Stream\Retry\InMemoryDeadLetterStore;use NextPDF\Pro\Stream\Retry\RetryPolicy;
$outputRoot = __DIR__ . '/out';\is_dir($outputRoot) || \mkdir($outputRoot, 0o775, true);
$engine = new InProcessRenderEngine(SingleDocumentRenderer::standalone(), maxBatchSize: 64);$committer = new LocalFilesystemCommitter($outputRoot);$deadLetter = new InMemoryDeadLetterStore();$retry = RetryPolicy::default(); // 3 attempts, 100ms base, 30s cap.
/** * Build one manifest and remember its output target for the commit stage. * * @return array{RenderManifest, OutputObjectKey} */$makeJob = static function (string $jobId, string $html): array { $target = OutputObjectKey::file('out', 'invoices/' . $jobId . '.pdf'); $manifest = RenderManifestBuilder::create($jobId) ->withInlineInput($html) ->withTemplate(TemplateRef::html()) ->withOutputKey($target) ->build();
return [$manifest, $target];};
/** @var array<non-empty-string, OutputObjectKey> $targets */$targets = [];$manifests = [];foreach (['inv-2001' => '<h1>2001</h1>', 'inv-2002' => '<h1>2002</h1>'] as $id => $html) { [$manifest, $target] = $makeJob($id, $html); $manifests[] = $manifest; $targets[$id] = $target;}
foreach ($engine->renderBatch($manifests) as $result) { // A timeout is transient — the policy decides whether to re-enqueue it. if ($result->status === EngineRenderStatus::Timeout && $retry->shouldRetry(1)) { // Re-enqueue on the caller's work queue after delayMsForAttempt(1) ms. continue; }
if (!$result->isRendered()) { $deadLetter->add(new DeadLetterRecord( jobId: $result->jobId, idempotencyKeyValue: $result->jobId, attempts: $retry->maxAttempts, lastErrorCode: $result->errorCode ?? 'SPEC-RENDER-EXCEPTION', lastErrorMessage: $result->errorMessage ?? '', failedAt: new DateTimeImmutable(), ));
continue; }
try { // overwrite=false: identical bytes are an idempotent no-op; divergent // bytes to an occupied key raise SPEC-COMMIT-409 instead of clobbering. $receipt = $committer->commit( $result->jobId, $targets[$result->jobId], $result->bytes, $result->sha256, ); } catch (OutputCommitConflictException $e) { $deadLetter->add(new DeadLetterRecord( jobId: $result->jobId, idempotencyKeyValue: $result->jobId, attempts: 1, lastErrorCode: $e->specCode(), lastErrorMessage: $e->getMessage(), failedAt: new DateTimeImmutable(), ));
continue; }
echo $receipt->idempotentReuse ? "reused {$receipt->target->toUri()}\n" : "committed {$receipt->target->toUri()}\n";}
if ($deadLetter->count() > 0) { \fwrite(\STDERR, $deadLetter->count() . " job(s) dead-lettered\n");}Khi nào dùng
Phần tiêu đề “Khi nào dùng”- Kết xuất batch khối lượng lớn nơi thông lượng hưởng lợi từ việc thực thi đồng thời (process-pool).
- Các lượt chạy kéo dài phải sống sót qua một sự cố và tiếp tục mà không xuất bản đầu ra hai lần.
- Các pipeline phải đảm bảo việc giao chính-xác-một-lần của mỗi tài liệu đã kết xuất tới đích của nó.
Với một tài liệu đơn lẻ tạm thời, hãy kết xuất trực tiếp bằng module Writer; giá trị của Stream nằm ở các batch bền bỉ, có thể tiếp tục, đồng thời.
Hiệu năng
Phần tiêu đề “Hiệu năng”Thông lượng tỉ lệ với số lượng worker trong ProcessPoolRenderUnitExecutor (bị giới hạn bởi maxWorkers và maxBatchSize), trong khi engine giữ đầu ra kết xuất byte-identical với baseline tuần tự. Một timeout theo thời gian-thực-tế giới hạn mỗi batch song song nên một worker bị treo không thể chặn mãi mãi. Không có con số thông lượng cố định nào được công bố; nó phụ thuộc vào độ phức tạp của tài liệu và mức song song của host. Hãy đo bằng các tài liệu đại diện.
Lưu ý bảo mật
Phần tiêu đề “Lưu ý bảo mật”Các manifest được xác thực theo lối fail-closed trước khi kết xuất. Committer từ chối path traversal, null byte, các scheme stream-wrapper, các đích là symlink, và vector luồng-dữ-liệu-thay-thế NTFS (dấu hai chấm), và phân giải mọi key dưới một root được cấu hình. Các kết quả worker liên-tiến-trình được băm lại và đối chiếu với digest mà worker báo cáo nên một worker bị nhiễu không thể làm hỏng đầu ra một cách âm thầm. Module này không ghi nhật ký nội dung tài liệu nào.
Lưu ý ranh giới Enterprise
Phần tiêu đề “Lưu ý ranh giới Enterprise”Các kho bền bỉ của Stream ở đây được hậu thuẫn bởi filesystem và đơn-host. Chính-xác-một-lần đồng thời liên-host tới cùng một key, và khử trùng lặp bền bỉ qua các lượt chạy, là việc của các committer và kho object-storage của Enterprise; bộ xử lý luồng document-job vốn điều khiển các cộng tác viên này là một mối quan tâm của Enterprise. Pro cung cấp engine, các hợp đồng, và các hiện thực bền bỉ cục bộ.
Phương án dự phòng / thay thế ở Core
Phần tiêu đề “Phương án dự phòng / thay thế ở Core”Không có Pro, hãy kết xuất các tài liệu lần lượt từng cái một với writer của NextPDF Core; luồng batch bền bỉ, thực thi đồng thời, và commit chính-xác-một-lần là các bổ sung của Pro. Xem /modules/writer/.
Ranh giới xuất bản
Phần tiêu đề “Ranh giới xuất bản”Trang này chỉ tài liệu hóa hành vi có thể quan sát từ bên ngoài và bề mặt API công khai được hỗ trợ. Các đường dẫn namespace nội bộ, các lớp trợ giúp, các bảng cơ chế, tên file runbook, và tiền tố ticket nằm ngoài phạm vi.