ข้ามไปยังเนื้อหา

การแก้ไขปัญหา Artisan

ความล้มเหลวของ bridge ทุกกรณีจะแสดงเป็น exception ที่ระบุชนิดไว้ ให้เทียบ exception และข้อความกับตารางด้านล่าง แต่ละแถวชี้ไปยังการตรวจสอบในซอร์สที่ทำให้เกิด exception นั้น เพื่อให้แก้ไขที่สาเหตุได้แทนการแก้เฉพาะอาการ

chrome-php/chrome is not installed. Install it via: composer require chrome-php/chrome:^1.15

ไลบรารีไคลเอนต์ของ Chrome DevTools Protocol (CDP) ไม่อยู่ใน autoloader BrowserPool::getBrowser() จะทำให้เกิด exception นี้ก่อนที่ Chrome จะเริ่มทำงาน ให้เรียกใช้คำสั่งติดตั้ง กรณีนี้ต่างจากกรณีที่ ไบนารี ขาดหายไป เพราะการตรวจสอบไลบรารีและการตรวจสอบไบนารีทำงานคนละขั้นตอนกัน

Chrome renderer failed: <cause>

Chrome ไม่สามารถเริ่มทำงาน หมดเวลา หรือเกิดข้อขัดข้อง exception ก่อนหน้าจะระบุสาเหตุต้นทาง ตรวจสอบสาเหตุที่พบบ่อยต่อไปนี้:

  • ไม่พบไบนารี / ไม่สามารถเรียกใช้งานได้ ตรวจสอบด้วย chromium --headless --dump-dom about:blank ตั้งค่า chrome_binary ให้เป็นพาธแบบสัมบูรณ์
  • Sandbox ไม่สามารถเริ่มต้นได้ (คอนเทนเนอร์) ข้อความสาเหตุจะกล่าวถึง sandbox หรือ namespace ใช้คอนเทนเนอร์ที่รองรับ sandbox เมื่อทำได้ หรือตั้งค่า no_sandbox: true หลังจากอ่าน /integrations/artisan/security-and-operations/
  • หมดเวลา เอกสารที่มีน้ำหนักมากใช้เวลาเกิน render_timeout เพิ่มค่าหมดเวลาสำหรับ workload นั้น หรือลดขนาดเอกสาร สำหรับเส้นทางที่ผู้ใช้เข้าถึงได้ ให้พิจารณาข้อแลกเปลี่ยนด้าน denial-of-service
  • ไลบรารีที่ใช้ร่วมกันขาดหายไป Chrome จะปิดการทำงานทันที ติดตั้งชุด dependency ของ Chrome สำหรับดิสทริบิวชันที่ใช้

Chrome printToPDF returned empty data

Chrome เริ่มทำงานแล้ว แต่ printToPDF ส่งคืนข้อมูลขนาดศูนย์ไบต์ สาเหตุที่พบบ่อยคืออินพุตไม่สร้าง box ที่เรนเดอร์ได้ เช่น body ว่างเปล่า เนื้อหาที่ตั้งค่าเป็น display:none หรือ Chrome เกิดข้อขัดข้องระหว่างการพิมพ์ ยืนยันว่าอินพุต Hypertext Markup Language (HTML) เรนเดอร์ออกมาเป็น box ที่มองเห็นได้ และตรวจสอบภาวะกดดันด้านหน่วยความจำของโฮสต์

ข้อความมีคำว่าสาเหตุวิธีแก้
exceeds maximum allowed sizeHTML มีขนาดเกิน maxHtmlSizeลดขนาดอินพุตหรือเพิ่ม max_html_size (ซึ่งขยายพื้นผิวการโจมตีแบบ resource-exhaustion)
oversized base64 data URIdata Uniform Resource Identifier (URI) แบบ base64 ที่ฝังในเนื้อหามีขนาด ≥ 13 MBลดขนาด asset ที่ฝังไว้ หรืออ้างอิงรูปภาพขนาดเล็กลง
forbidden meta refresh redirectพบ <meta http-equiv="refresh">ลบแท็กดังกล่าวออก เนื่องจากเป็น navigation vector แบบ server-side request forgery (SSRF) และจะถูกปฏิเสธเสมอ

ข้อผิดพลาดเหล่านี้มาจาก ChromeSecurityPolicy::validate() และเกิดขึ้นก่อนติดต่อ Chrome จึงครอบคลุมด้วยการทดสอบได้รวดเร็วและมีต้นทุนต่ำ

Page <n> has no content stream

Chrome สร้างไฟล์ Portable Document Format (PDF) ที่ parser ไม่สามารถแยกหน้าออกมาได้ กรณีนี้พบได้ยาก และมักบ่งชี้ว่าเอาต์พุตของ Chrome ผิดรูปแบบ หรือ Chrome เวอร์ชันนั้นสร้างโครงสร้างที่ไม่คาดคิด ให้บันทึกเวอร์ชันของ Chrome และอินพุตไว้ ตรวจสอบว่าไบนารีเป็น build ของ Chrome/Chromium ที่รองรับ

กรณีนี้ไม่ใช่บั๊กของ bridge เนื่องจาก bridge จะบล็อกการ fetch subresource ทุกครั้งด้วย Content Security Policy (CSP) default-src 'none' และการบล็อกแบบ CDP setBlockedURLs('*') <img> สไตล์ชีต ฟอนต์ สคริปต์ และ iframe แบบรีโมตจึงไม่โหลด ให้ฝัง asset ในเนื้อหาเป็น URI แบบ data: และฝัง Cascading Style Sheets (CSS) ในเนื้อหาผ่าน defaultCss หรือ <style> ดูโมเดลเครือข่ายได้ที่ /integrations/artisan/security-and-operations/

เอกสารล้นไปยังหน้าที่สองของ Chrome และ bridge นำเข้าเฉพาะหน้า 0 เท่านั้น บัฟเฟอร์ auto-fit เล็กเกินไปสำหรับการ reflow ที่สูงผิดปกติ หรือความสูงที่กำหนดไว้อย่างชัดเจนน้อยเกินไป กำหนดความสูงอย่างชัดเจนให้พอดีกับเนื้อหา หรือลบความสูงที่กำหนดออกเพื่อใช้ auto-fit พร้อมบัฟเฟอร์ความปลอดภัย ดูการจัดการความสูงได้ที่ /integrations/artisan/production-usage/

กรณีนี้เป็นพฤติกรรมที่คาดไว้ BrowserPool รีสตาร์ท Chrome ทุก ๆ 100 การเรนเดอร์เพื่อจำกัดการใช้หน่วยความจำ บรรทัด log ระดับ notice จะบันทึกการรีสตาร์ทและจำนวนการเรนเดอร์ ให้ถือว่านี่เป็นต้นทุนตามรอบที่คาดการณ์ได้ใน service-level objectives (SLOs) ไม่ใช่เหตุการณ์ผิดปกติ อัตราการรีสตาร์ทที่ถี่กว่าทุก ๆ 100 การเรนเดอร์หมายความว่าเอกสารมีน้ำหนักมากกว่าที่คาดไว้

BrowserPool จำกัดการเพิ่มขึ้นของหน่วยความจำด้วยการรีสตาร์ททุก 100 การเรนเดอร์ แต่ worker ที่ทำงานเป็นเวลานานมากยังสะสมหน่วยความจำได้ ให้เรียก close() ระหว่าง batch ขนาดใหญ่เพื่อรีไซเคิล Chrome ตั้งแต่เนิ่น ๆ และเรียกใช้ worker ภายใต้ขีดจำกัดหน่วยความจำของโฮสต์

  1. ทำซ้ำปัญหาด้วยส่วนย่อย HTML ที่เชื่อถือได้และเล็กที่สุด เพื่อแยกปัญหาด้านอินพุตออกจากปัญหาด้านสภาพแวดล้อม
  2. เรียกใช้ chromium --headless --dump-dom about:blank บนโฮสต์ในฐานะผู้ใช้ของ worker
  3. เพิ่ม logger แบบ PHP Standards Recommendation 3 (PSR-3) และอ่านบรรทัด info/notice ซึ่งมีพาธของไบนารีและจำนวนการรีสตาร์ท
  4. ยืนยันว่าติดตั้ง chrome-php/chrome แล้ว: BrowserPool::getBrowser() จะไม่ throw ChromeNotAvailableException เมื่อ chrome-php/chrome พร้อมใช้งาน
  5. ตรวจสอบ exception ก่อนหน้าที่แนบมากับ exception นั้นเพื่อหาสาเหตุต้นทางจาก Chrome
  • การติดตั้ง Artisan: /integrations/artisan/install/
  • การกำหนดค่า Artisan: /integrations/artisan/configuration/
  • ความปลอดภัยและการดำเนินงาน: /integrations/artisan/security-and-operations/
  • การตั้งค่าตัวเรนเดอร์ Chrome: /integrations/artisan/chrome-renderer-setup/
  • การใช้งานจริง: /integrations/artisan/production-usage/