วิธีเริ่มต้นใช้งาน VUE3.0 อย่างรวดเร็ว: เข้าสู่การเรียนรู้
หลังจากที่บริการของเราเปิดตัวแล้ว สภาพแวดล้อมที่ทำงานอยู่จะถูกกำหนดเวลาอย่างหลีกเลี่ยงไม่ได้ (เช่น คอนเทนเนอร์, pm2 เป็นต้น) การอัปเกรดบริการจะทำให้รีสตาร์ท และข้อยกเว้นต่างๆ จะทำให้ กระบวนการที่จะล้มเหลว โดยทั่วไป สภาพแวดล้อมที่ทำงานอยู่จะมีการตรวจสอบสุขภาพของกระบวนการบริการจะรีสตาร์ทกระบวนการเมื่อกระบวนการผิดปกติ อย่างไรก็ตาม กลยุทธ์การจัดกำหนดการของสภาพแวดล้อมที่ทำงานอยู่จะถือว่ากระบวนการบริการของเราเป็นเหมือนกล่องดำ และไม่สนใจสภาพการทำงานภายในของกระบวนการบริการ ดังนั้น กระบวนการบริการของเราจำเป็นต้องรับรู้ถึงการดำเนินการตามกำหนดการของสภาพแวดล้อมที่ทำงานอยู่อย่างจริงจัง จากนั้นจึงดำเนินการ ออกจากการดำเนินการล้างข้อมูลบางส่วน
ดังนั้นวันนี้เราจะแยกแยะสถานการณ์ต่างๆ ที่อาจทำให้กระบวนการ Node.js ออก และสิ่งที่เราสามารถทำได้โดยการฟังเหตุการณ์การออกจากกระบวนการเหล่านี้
หลักการ:
เมื่อกระบวนการต้องการออก จะไม่มีสิ่งใดมากไปกว่าสองสถานการณ์ หนึ่งคือ กระบวนการออกอย่างแข็งขัน และอีกประการหนึ่งคือ ได้รับสัญญาณของระบบที่กำหนดให้กระบวนการออก
ออกจากการแจ้งเตือนสัญญาณระบบ
สัญญาณระบบทั่วไปแสดงอยู่ในเอกสารอย่างเป็นทางการของ Node.js โดยหลักๆ แล้วเราจะเน้นไปที่สัญญาณบางส่วน:
เมื่อได้รับสัญญาณออกที่ไม่บังคับ กระบวนการ Node.js จะสามารถฟังสัญญาณทางออกและดำเนินการตรรกะการออกแบบกำหนดเองบางอย่างได้ ตัวอย่างเช่น เราเขียนเครื่องมือ cli ที่ใช้เวลานานในการดำเนินการงาน หากผู้ใช้ต้องการออกจากกระบวนการโดยการกด Ctrl+c ก่อนที่งานจะเสร็จสิ้น ผู้ใช้จะได้รับแจ้งให้รอ:
const readline = need(' อ่านบรรทัด'); process.on('SIGINT', () => { // เราใช้ readline เพื่อใช้งานการโต้ตอบในบรรทัดคำสั่ง const rl = readline.createInterface({ อินพุต: กระบวนการ.stdin, เอาท์พุต: process.stdout - rl.question('งานยังไม่เสร็จสิ้น คุณแน่ใจหรือไม่ว่าต้องการออก', answer => { ถ้า (ตอบ === 'ใช่') { console.log('การดำเนินการงานถูกขัดจังหวะ, ออกจากกระบวนการ'); กระบวนการออก (0); } อื่น { console.log('งานดำเนินต่อไป...'); - rl.ปิด(); - - // จำลองงานที่ใช้เวลา 1 นาทีในการดำเนินการ const longTimeTask = () => { console.log('เริ่มงาน...'); setTimeout(() => { console.log('สิ้นสุดงาน'); }, 1,000 * 60); - longTimeTask();
ทุกครั้งที่กด ctrl + c ผู้ใช้จะได้รับแจ้ง:
กระบวนการออกจาก
Node.js อย่างแข็งขัน โดยส่วนใหญ่รวมถึงสถานการณ์ต่อไปนี้:
เรารู้ว่า pm2 มีผลกระทบต่อกระบวนการ daemon ในกรณีของคุณ เมื่อกระบวนการของคุณออกโดยมีข้อผิดพลาด pm2 จะรีสตาร์ทกระบวนการของคุณ กระบวนการลูกในโหมดคลัสเตอร์ของ Node.js (จริงๆ แล้ว pm2 มีตรรกะที่คล้ายกัน):
constคลัสเตอร์ = need('cluster' ); const http = ต้องการ ('http'); const numCPUs = ต้องการ ('os'). cpus (). ความยาว; กระบวนการ const = ต้องการ ('กระบวนการ'); // รหัสกระบวนการหลักถ้า (cluster.isMaster) { console.log(`เริ่มกระบวนการหลัก: ${process.pid}`); // ขึ้นอยู่กับจำนวนคอร์ cpu ให้สร้างกระบวนการทำงานสำหรับ (ให้ i = 0; i < numCPUs; i++) { คลัสเตอร์.ส้อม(); - // ฟังเหตุการณ์ออกจากกระบวนการของผู้ปฏิบัติงานคลัสเตอร์on('exit', (ผู้ปฏิบัติงาน, รหัส, สัญญาณ) => { console.log(`ออกจากกระบวนการของผู้ปฏิบัติงาน ${worker.process.pid} แล้ว รหัสข้อผิดพลาด: ${code || signal} กำลังรีสตาร์ท...`); //รีสตาร์ทกระบวนการลูกคลัสเตอร์.fork(); - - // รหัสกระบวนการของผู้ปฏิบัติงานถ้า (cluster.isWorker) { // ฟังเหตุการณ์ข้อผิดพลาดที่ไม่ได้ตรวจสอบ process.on('uncaughtException', error => { console.log(`เกิดข้อผิดพลาดในกระบวนการของผู้ปฏิบัติงาน ${process.pid}`, ข้อผิดพลาด); process.emit('ตัดการเชื่อมต่อ'); กระบวนการทางออก (1); - //สร้างเว็บเซิร์ฟเวอร์ // แต่ละกระบวนการของผู้ปฏิบัติงานจะฟังพอร์ต 8000 (Node.js จะจัดการภายในและจะไม่ทำให้เกิดความขัดแย้งของพอร์ต) http.createServer((req, res) => { res.writeHead(200); res.end('สวัสดีชาวโลกn'); }).ฟัง(8000); console.log(`เริ่มกระบวนการของผู้ปฏิบัติงาน: ${process.pid}`); }
แนวทางปฏิบัติของแอปพลิเคชัน
สถานการณ์ต่างๆ ที่กระบวนการ Node.js ได้รับการวิเคราะห์ข้างต้น ตอนนี้เราจะสร้างเครื่องมือสำหรับตรวจสอบการออกจากกระบวนการ เมื่อกระบวนการ Node.js ออกจากระบบ ผู้ใช้จะได้รับอนุญาตให้ดำเนินการตรรกะทางออกของตนเอง:
// exit-hook.js //บันทึกงานทางออกที่ต้องดำเนินการ const งาน = []; //เพิ่มงานทางออก const addExitTask = fn => Tasks.push(fn); const handleExit = (รหัส ข้อผิดพลาด) => { // ...การใช้งาน handleExit แสดงไว้ด้านล่าง}; //ฟังเหตุการณ์ทางออกต่างๆ process.on('exit', code => handleExit(code)); // ตามข้อกำหนด POSIX เราใช้หมายเลขสัญญาณ 128 + เพื่อรับรหัสทางออกสุดท้าย // โปรดดูภาพด้านล่างสำหรับหมายเลขสัญญาณ คุณสามารถดำเนินการ kill -l ภายใต้ระบบ Linux เพื่อดูกระบวนการหมายเลขสัญญาณทั้งหมด ('SIGHUP', () => handleExit(128 + 1)); process.on('SIGINT', () => handleExit(128 + 2)); process.on('SIGTERM', () => handleExit(128 + 15)); // กด ctrl+break เพื่อออกจากกระบวนการสัญญาณ on('SIGBREAK', () => handleExit(128 + 21)); // รหัสออก 1 แสดงถึงข้อผิดพลาดที่ไม่ได้ตรวจจับซึ่งทำให้กระบวนการออกจากกระบวนการ on('uncaughtException', error => handleExit(1, error)); process.on('unhandledRejection', error => handleExit(1, error))
;
ต่อไปเราจำเป็นต้องใช้ฟังก์ชัน exit ของกระบวนการจริง handleExit เนื่องจากฟังก์ชันงานที่ส่งผ่านโดยผู้ใช้อาจเป็นแบบซิงโครนัสหรือแบบอะซิงโครนัส เราสามารถใช้ process.nextTick เพื่อให้แน่ใจว่าโค้ดการซิงโครไนซ์ของผู้ใช้ได้รับการดำเนินการ ซึ่งสามารถเข้าใจกระบวนการได้ง่าย .nextTick จะถูกดำเนินการหลังจากการเรียกใช้โค้ดซิงโครนัสในแต่ละขั้นตอนลูปเหตุการณ์เสร็จสมบูรณ์ (เข้าใจ process.nextTick) สำหรับงานอะซิงโครนัส เราต้องการให้ผู้ใช้โทรกลับเพื่อแจ้งให้เราทราบว่างานอะซิงโครนัสเสร็จสมบูรณ์แล้ว:
// ทำเครื่องหมายว่า กำลังออก หลีกเลี่ยงการดำเนินการหลายครั้งของ la isExiting = false; const handleExit = (รหัส ข้อผิดพลาด) => { ถ้า (isExiting) กลับมา; กำลังออก = จริง; // ทำเครื่องหมายว่ามีการดำเนินการออกเพื่อหลีกเลี่ยงการเรียกหลายครั้งเพื่อให้ hasDoExit = fasle; const doExit = () => { ถ้า (hasDoExit) กลับมา; hasDoExit = จริง process.nextTick(() => กระบวนการทางออก (รหัส)) - // บันทึกจำนวนงานอะซิงโครนัสที่มีให้ asyncTaskCount = 0; // หลังจากที่งานอะซิงโครนัสสิ้นสุดลง การโทรกลับที่ผู้ใช้จำเป็นต้องโทรให้ la ayncTaskCallback = () => { process.nextTick(() => { asyncTaskCount-- ถ้า (asyncTaskCount === 0) doExit() - - //ดำเนินการภารกิจทางออกทั้งหมดforEach(taskFn => { // หากจำนวนพารามิเตอร์ของฟังก์ชัน taskFn มากกว่า 1 จะถือว่าพารามิเตอร์การเรียกกลับถูกส่งไปและเป็นงานแบบอะซิงโครนัสถ้า (taskFn.length > 1) { asyncTaskCount++ TaskFn (ข้อผิดพลาด ayncTaskCallback) } อื่น { TaskFn (ข้อผิดพลาด) - - // หากมีงานอะซิงโครนัสถ้า (asyncTaskCount > 0) { // หลังจากผ่านไปมากกว่า 10 วินาที ให้บังคับออกจาก setTimeout(() => { ทำออก(); }, 10 * 1,000) } อื่น { ทำออก() - };
ณ จุดนี้ เครื่องมือตรวจสอบการออกจากกระบวนการของเราเสร็จสมบูรณ์ คุณสามารถดูกระบวนการนี้ แบบ async-exit-hook ได้
อย่าง
สมบูรณ์ ออกจาก
เว็บเซิร์ฟเวอร์ของเรา เมื่อรีสตาร์ท ถูกกำหนดเวลาโดยคอนเทนเนอร์ที่ทำงานอยู่ (pm2 หรือนักเทียบท่า ฯลฯ) หรือเมื่อมีข้อยกเว้นเกิดขึ้นและกระบวนการออก เราหวังว่าจะดำเนินการออก เช่น ดำเนินการตอบสนองต่อคำขอที่เชื่อมต่อกับ บริการ, ทำความสะอาดการเชื่อมต่อฐานข้อมูล, พิมพ์บันทึกข้อผิดพลาด, เรียกใช้การแจ้งเตือน ฯลฯ หลังจากเสร็จสิ้นการดำเนินการออกแล้วออกจากกระบวนการ เราสามารถใช้เครื่องมือตรวจสอบการออกจากกระบวนการเพื่อนำไปใช้:
const http = need(' http'); //สร้างเว็บเซิร์ฟเวอร์ เซิร์ฟเวอร์ const = http.createServer ((req, res) => { res.writeHead(200); res.end('สวัสดีชาวโลกn'); }).ฟัง(8000); // ใช้เครื่องมือที่เราพัฒนาด้านบนเพื่อเพิ่มงานออกจากกระบวนการ addExitTask((error, callback) => { // พิมพ์บันทึกข้อผิดพลาด, แจ้งเตือน, ปล่อยการเชื่อมต่อฐานข้อมูล ฯลฯ console.log ('กระบวนการออกอย่างผิดปกติ' ข้อผิดพลาด) // หยุดรับคำขอใหม่ server.close((error) => { ถ้า (ข้อผิดพลาด) { console.log('หยุดรับคำขอใหม่ผิดพลาด' ข้อผิดพลาด) } อื่น { console.log('หยุดรับคำขอใหม่') - - // วิธีที่ง่ายกว่าคือการรอช่วงระยะเวลาหนึ่ง (ที่นี่เรารอ 5 วินาที) เพื่อให้คำขอที่มีอยู่เสร็จสมบูรณ์ // หากคุณต้องการให้แน่ใจว่าคำขอทั้งหมดได้รับการประมวลผลอย่างสมบูรณ์ คุณต้องบันทึกการเชื่อมต่อแต่ละรายการและรอจนกระทั่ง การเชื่อมต่อทั้งหมดจะถูกปล่อยออกมา ดำเนินการออกคำสั่ง // คุณสามารถอ้างถึงไลบรารีโอเพ่นซอร์ส https://github.com/sebhildebrandt/http-graceful-shutdown setTimout (โทรกลับ, 5 * 1,000) })
สรุป
จากข้อความข้างต้น ฉันเชื่อว่าคุณคงทราบถึงสถานการณ์ต่างๆ ที่ทำให้กระบวนการ Node.js หยุดทำงานแล้ว หลังจากที่บริการออนไลน์แล้ว แม้ว่าเครื่องมือเช่น k8s และ pm2 จะสามารถดึงกระบวนการขึ้นมาได้อย่างต่อเนื่องเมื่อกระบวนการออกอย่างผิดปกติเพื่อให้แน่ใจว่ามีความพร้อมใช้งานของบริการ เราควรรับรู้ถึงความผิดปกติหรือการกำหนดเวลาของกระบวนการในโค้ดอย่างจริงจัง ดังนั้น เพื่อให้สามารถจับปัญหาได้ล่วงหน้า