การใช้งาน Artisan ใน production
ภาพรวมโดยย่อ
หัวข้อที่มีชื่อว่า “ภาพรวมโดยย่อ”ในการใช้งานจริง ให้ inject renderer ที่กำหนดค่าไว้แล้วพร้อม logger แบบ PHP Standards Recommendation 3 (PSR-3) นำกระบวนการ Chrome ที่ทำงานอยู่กลับมาใช้ซ้ำระหว่างการเรนเดอร์ ระบุความสูงให้ชัดเจนสำหรับเอกสารที่มีหลายองค์ประกอบ และจำกัดเส้นทางการเรนเดอร์ด้วย timeout จากต้นทาง
ภาพรวมเชิงแนวคิด
หัวข้อที่มีชื่อว่า “ภาพรวมเชิงแนวคิด”BrowserPool รักษากระบวนการ Chrome หนึ่งกระบวนการให้ทำงานอยู่ (keepAlive: true) และรีสตาร์ตทุก 100 การเรนเดอร์เพื่อจำกัดการเติบโตของหน่วยความจำ ซึ่งเป็นรูปแบบการสะสมที่ทราบกันดีในไคลเอนต์ Chrome DevTools Protocol (CDP) ที่ทำงานเป็นเวลานาน สำหรับ worker ที่เรนเดอร์เอกสารจำนวนมาก ให้ใช้ renderer ที่ทำงานยาวนานหนึ่งตัวแทนการสร้าง renderer หนึ่งตัวต่อหนึ่งคำขอ เพื่อลดต้นทุนการเริ่มต้น 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(); }}สร้าง renderer หนึ่งครั้งแล้วนำกลับมาใช้ซ้ำ เรียก close() เมื่อ worker ปิดตัวลงเพื่อปล่อยกระบวนการ Chrome อย่างคาดการณ์ได้ แทนการรอ destructor ส่วน catch ทั้งสองแยกข้อผิดพลาดของการ deploy (ขาด runtime) ออกจากข้อผิดพลาดขณะเรนเดอร์ (ลองใหม่ได้) อย่าใช้บล็อก catch ที่ว่างเปล่า
ผูกเข้ากับ container ในรูปแบบ singleton:
$container->singleton(ReportRenderer::class, fn ($c) => new ReportRenderer($c->get(Psr\Log\LoggerInterface::class)));การจัดการความสูง
หัวข้อที่มีชื่อว่า “การจัดการความสูง”เมื่อละเว้นความสูง bridge จะวัดความสูงของเนื้อหาใน Chrome (ค่า max ของความสูง scroll และ offset ของ body/document) แปลงเป็นพอยต์ แล้วเพิ่ม safety buffer ประมาณ 0.2 นิ้ว (~14.4 pt) buffer นี้ครอบคลุมความต่างระหว่างเค้าโครงแบบ viewport ของ Chrome กับการ reflow ในเค้าโครงสำหรับการพิมพ์ หากไม่มี buffer นี้ printToPDF อาจดันเนื้อหาไปยังหน้าที่สอง ซึ่ง PageImporter (หน้า 0 เท่านั้น) จะตัดทิ้ง bridge บังคับให้ความสูงกระดาษขั้นต่ำอยู่ที่ 0.1 นิ้ว การทดสอบ ChromeHtmlRendererTest::renderUsesAutoFitHeightByDefault, ::renderAutoFitBufferIsAddedNotSubtracted และ ::renderAppliesMinimumHeightOf0Point1InchForTinyExplicitHeight ยืนยันพฤติกรรมนี้
สำหรับเอกสารที่มีเค้าโครงตายตัว (ใบแจ้งหนี้ ใบรับรอง) ให้ส่งความสูงเป็นพอยต์อย่างชัดเจน เมื่อระบุความสูงอย่างชัดเจน จะไม่มีการเพิ่ม buffer และผลลัพธ์จะตรงกับขนาดกระดาษที่ร้องขอพอดี (ยืนยันโดย ::renderHonorsExplicitHeightWithoutAutoBuffer)
ตัวประมวลผลแบบ Batch
หัวข้อที่มีชื่อว่า “ตัวประมวลผลแบบ Batch”- สร้าง renderer หนึ่งตัวต่อ worker หนึ่งตัว แล้วนำกลับมาใช้ซ้ำ
BrowserPoolนำเบราว์เซอร์ที่ทำงานอยู่กลับมาใช้ซ้ำและรีสตาร์ตโดยอัตโนมัติเมื่อครบขอบเขต 100 การเรนเดอร์ - เรียก
close()เมื่อ worker ปิดตัว และระหว่าง batch ขนาดใหญ่เมื่อต้องการกระบวนการ Chrome ใหม่เร็วกว่าขอบเขต 100 การเรนเดอร์ - destructor จะเรียก
close()แต่การเรียกclose()อย่างชัดเจนให้ผลที่คาดการณ์ได้และเหมาะกับกระบวนการที่ทำงานยาวนาน - ระบบจะบันทึกการแจ้งเตือนการรีสตาร์ตที่ระดับ
noticeพร้อมจำนวนการเรนเดอร์ ให้ตั้งการแจ้งเตือนเมื่ออัตราการรีสตาร์ตสูงขึ้น เพราะบ่งชี้ว่าเอกสารหนักกว่าที่คาดไว้
ความสามารถในการสังเกตการณ์ (Observability)
หัวข้อที่มีชื่อว่า “ความสามารถในการสังเกตการณ์ (Observability)”ให้ inject logger แบบ PSR-3 แล้ว renderer จะส่งเหตุการณ์และระดับต่อไปนี้:
| เหตุการณ์ | ระดับ | บริบท |
|---|---|---|
| เริ่มการเรนเดอร์ | debug | size, width, height |
| การเรนเดอร์เสร็จสมบูรณ์ | debug | pdfSize, contentHeight |
| เปิดเบราว์เซอร์ | info | binary |
| รีสตาร์ตเบราว์เซอร์ | notice | count |
| ปิดเบราว์เซอร์ | debug | renderCount |
ไม่มีการบันทึก HTML ไบต์ของ PDF หรือข้อความที่สกัดออกมา วิธีนี้ช่วยกันไม่ให้ payload เข้าไปอยู่ใน log ของการดำเนินงาน และสอดคล้องกับแนวทางเนื้อหา log ตาม National Institute of Standards and Technology Special Publication (NIST SP) 800-92 สร้าง service-level objective (SLO) ของ latency จากคู่ start/complete และสร้างการแจ้งเตือนอัตราการรีสตาร์ตจากเหตุการณ์ notice ที่บันทึกไว้
รูปแบบการ deploy
หัวข้อที่มีชื่อว่า “รูปแบบการ deploy”- Sidecar Chrome: เรียกใช้ Chrome ใน container เดียวกับ PHP worker ตรึงค่า
chrome_binaryและจัดเตรียม container ที่รองรับ sandbox ดูที่ /integrations/artisan/chrome-renderer-setup/ - Containerless / CLI: Artisan ไม่มี dependency injection container ใช้
EInvoiceServiceFactoryสำหรับสัญญา e-invoice ของ Premium ใน runner แบบ command-line interface (CLI) ดูที่ /integrations/artisan/boot-and-discovery/ - Resource bounding: จับคู่
render_timeoutกับงบประมาณคำขอจากต้นทางและ cgroup/ulimit ของโฮสต์ ดูแบบจำลองภัยคุกคามที่ /integrations/artisan/security-and-operations/
กรณีขอบและข้อควรระวัง
หัวข้อที่มีชื่อว่า “กรณีขอบและข้อควรระวัง”- แม้ renderer จะถูกขัดจังหวะกลางการเรนเดอร์ ก็ยังคงปิดหน้า Chrome (
finally) และ pool ยังคงใช้งานได้ - ไม่รองรับการนำ renderer หนึ่งตัวกลับมาใช้ซ้ำข้าม threads/processes renderer หนึ่งตัวเป็นเจ้าของกระบวนการ Chrome หนึ่งกระบวนการ
- การรีสตาร์ตเมื่อครบ 100 การเรนเดอร์เป็นค่าตายตัว ให้กำหนดขนาด batch โดยคำนึงถึงเรื่องนี้เพื่อให้การพุ่งขึ้นของ latency คาดการณ์ได้
ประสิทธิภาพ
หัวข้อที่มีชื่อว่า “ประสิทธิภาพ”ในสภาวะคงตัว ต้นทุนคือการจัดเค้าโครง input ของ Chrome บวกกับ printToPDF ไม่ใช่ overhead ของ bridge keepAlive เฉลี่ยต้นทุนการเริ่มต้นออกไปตลอดการเรนเดอร์ คาดว่าจะมีการพุ่งขึ้นของ latency ในการเรนเดอร์ทุกครั้งที่ 100 (การรีสตาร์ตกระบวนการ) ให้สะท้อนเรื่องนี้ไว้ใน SLO แทนที่จะถือว่าเป็นเหตุการณ์ผิดปกติ
หมายเหตุด้านความปลอดภัย
หัวข้อที่มีชื่อว่า “หมายเหตุด้านความปลอดภัย”เส้นทางการใช้งานจริงรับ HTML ที่ไม่น่าเชื่อถือ อ่าน /integrations/artisan/security-and-operations/ ซ้ำอีกครั้ง มาตรการกั้นเครือข่ายยังคงทำงานไม่ว่าจะกำหนดค่าอย่างไร แต่ no_sandbox: true จะลบการแยกกระบวนการของ Chrome ออกไปและเพิ่มข้อกำหนดด้านความน่าเชื่อถือต่อ input
บริบทเชิงพาณิชย์
หัวข้อที่มีชื่อว่า “บริบทเชิงพาณิชย์”ใน worker แบบ containerless EInvoiceServiceFactory จะคืนค่า null เมื่อยังไม่ได้ติดตั้ง Premium ดังนั้นเส้นทางการเรนเดอร์แบบโอเพนซอร์สจึงทำงานต่อไปโดยไม่เปลี่ยนแปลง ติดตั้ง Pro/Enterprise เพื่อเปิดใช้การฝังและการตรวจสอบ e-invoice บนเอกสารที่เรนเดอร์แล้ว
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- การเริ่มต้นใช้งาน Artisan (/integrations/artisan/quickstart/)
- การกำหนดค่า Artisan (/integrations/artisan/configuration/)
- ความปลอดภัยและการดำเนินงาน Artisan (/integrations/artisan/security-and-operations/)
- การตั้งค่า Chrome renderer (/integrations/artisan/chrome-renderer-setup/)
- การแก้ไขปัญหา Artisan (/integrations/artisan/troubleshooting/)