การแก้ไขปัญหา Artisan
ภาพรวมโดยย่อ
หัวข้อที่มีชื่อว่า “ภาพรวมโดยย่อ”ความล้มเหลวของ bridge ทุกกรณีจะแสดงเป็น exception ที่ระบุชนิดไว้ ให้เทียบ exception และข้อความกับตารางด้านล่าง แต่ละแถวชี้ไปยังการตรวจสอบในซอร์สที่ทำให้เกิด exception นั้น เพื่อให้แก้ไขที่สาเหตุได้แทนการแก้เฉพาะอาการ
วินิจฉัยตาม exception
หัวข้อที่มีชื่อว่า “วินิจฉัยตาม exception”ChromeNotAvailableException
หัวข้อที่มีชื่อว่า “ChromeNotAvailableException”
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 จะเริ่มทำงาน ให้เรียกใช้คำสั่งติดตั้ง กรณีนี้ต่างจากกรณีที่ ไบนารี ขาดหายไป เพราะการตรวจสอบไลบรารีและการตรวจสอบไบนารีทำงานคนละขั้นตอนกัน
ChromeRenderException — การเริ่มทำงานหรือหมดเวลา
หัวข้อที่มีชื่อว่า “ChromeRenderException — การเริ่มทำงานหรือหมดเวลา”
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 สำหรับดิสทริบิวชันที่ใช้
ChromeRenderException — เอาต์พุตว่างเปล่า
หัวข้อที่มีชื่อว่า “ChromeRenderException — เอาต์พุตว่างเปล่า”
Chrome printToPDF returned empty data
Chrome เริ่มทำงานแล้ว แต่ printToPDF ส่งคืนข้อมูลขนาดศูนย์ไบต์ สาเหตุที่พบบ่อยคืออินพุตไม่สร้าง box ที่เรนเดอร์ได้ เช่น body ว่างเปล่า เนื้อหาที่ตั้งค่าเป็น display:none หรือ Chrome เกิดข้อขัดข้องระหว่างการพิมพ์ ยืนยันว่าอินพุต Hypertext Markup Language (HTML) เรนเดอร์ออกมาเป็น box ที่มองเห็นได้ และตรวจสอบภาวะกดดันด้านหน่วยความจำของโฮสต์
RuntimeException — อินพุตถูกปฏิเสธก่อนถึง Chrome
หัวข้อที่มีชื่อว่า “RuntimeException — อินพุตถูกปฏิเสธก่อนถึง Chrome”| ข้อความมีคำว่า | สาเหตุ | วิธีแก้ |
|---|---|---|
exceeds maximum allowed size | HTML มีขนาดเกิน maxHtmlSize | ลดขนาดอินพุตหรือเพิ่ม max_html_size (ซึ่งขยายพื้นผิวการโจมตีแบบ resource-exhaustion) |
oversized base64 data URI | data 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 จึงครอบคลุมด้วยการทดสอบได้รวดเร็วและมีต้นทุนต่ำ
PdfParseException
หัวข้อที่มีชื่อว่า “PdfParseException”
Page <n> has no content stream
Chrome สร้างไฟล์ Portable Document Format (PDF) ที่ parser ไม่สามารถแยกหน้าออกมาได้ กรณีนี้พบได้ยาก และมักบ่งชี้ว่าเอาต์พุตของ Chrome ผิดรูปแบบ หรือ Chrome เวอร์ชันนั้นสร้างโครงสร้างที่ไม่คาดคิด ให้บันทึกเวอร์ชันของ Chrome และอินพุตไว้ ตรวจสอบว่าไบนารีเป็น build ของ Chrome/Chromium ที่รองรับ
asset แบบรีโมตเรนเดอร์ออกมาว่างเปล่า
หัวข้อที่มีชื่อว่า “asset แบบรีโมตเรนเดอร์ออกมาว่างเปล่า”กรณีนี้ไม่ใช่บั๊กของ 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/
latency พุ่งสูงทุก ๆ ประมาณ 100 การเรนเดอร์
หัวข้อที่มีชื่อว่า “latency พุ่งสูงทุก ๆ ประมาณ 100 การเรนเดอร์”กรณีนี้เป็นพฤติกรรมที่คาดไว้ BrowserPool รีสตาร์ท Chrome ทุก ๆ 100 การเรนเดอร์เพื่อจำกัดการใช้หน่วยความจำ บรรทัด log ระดับ notice จะบันทึกการรีสตาร์ทและจำนวนการเรนเดอร์ ให้ถือว่านี่เป็นต้นทุนตามรอบที่คาดการณ์ได้ใน service-level objectives (SLOs) ไม่ใช่เหตุการณ์ผิดปกติ อัตราการรีสตาร์ทที่ถี่กว่าทุก ๆ 100 การเรนเดอร์หมายความว่าเอกสารมีน้ำหนักมากกว่าที่คาดไว้
หน่วยความจำเพิ่มขึ้นตลอด batch ที่ยาวนาน
หัวข้อที่มีชื่อว่า “หน่วยความจำเพิ่มขึ้นตลอด batch ที่ยาวนาน”BrowserPool จำกัดการเพิ่มขึ้นของหน่วยความจำด้วยการรีสตาร์ททุก 100 การเรนเดอร์ แต่ worker ที่ทำงานเป็นเวลานานมากยังสะสมหน่วยความจำได้ ให้เรียก close() ระหว่าง batch ขนาดใหญ่เพื่อรีไซเคิล Chrome ตั้งแต่เนิ่น ๆ และเรียกใช้ worker ภายใต้ขีดจำกัดหน่วยความจำของโฮสต์
รายการตรวจสอบสำหรับการวินิจฉัย
หัวข้อที่มีชื่อว่า “รายการตรวจสอบสำหรับการวินิจฉัย”- ทำซ้ำปัญหาด้วยส่วนย่อย HTML ที่เชื่อถือได้และเล็กที่สุด เพื่อแยกปัญหาด้านอินพุตออกจากปัญหาด้านสภาพแวดล้อม
- เรียกใช้
chromium --headless --dump-dom about:blankบนโฮสต์ในฐานะผู้ใช้ของ worker - เพิ่ม logger แบบ PHP Standards Recommendation 3 (PSR-3) และอ่านบรรทัด
info/noticeซึ่งมีพาธของไบนารีและจำนวนการรีสตาร์ท - ยืนยันว่าติดตั้ง
chrome-php/chromeแล้ว:BrowserPool::getBrowser()จะไม่ throwChromeNotAvailableExceptionเมื่อ chrome-php/chrome พร้อมใช้งาน - ตรวจสอบ 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/