كيفية البدء بسرعة مع VUE3.0: أدخل إلى التعلم
بعد إصدار خدمتنا، ستتم جدولتها حتمًا بواسطة بيئة التشغيل (مثل الحاويات، وpm2، وما إلى ذلك)، وستؤدي ترقيات الخدمة إلى إعادة التشغيل، وستؤدي الاستثناءات المختلفة إلى حدوث ذلك. بشكل عام، تعطلت العملية؛ ومع ذلك، فإن استراتيجية الجدولة لبيئة التشغيل تتعامل مع عملية الخدمة الخاصة بنا على أنها صندوق أسود ولا تهتم بظروف التشغيل الداخلية لعملية الخدمة، لذلك تحتاج عملية الخدمة لدينا إلى استشعار إجراءات الجدولة لبيئة التشغيل بشكل نشط ثم تنفيذها بعض إجراءات تنظيف الخروج.
لذا، سنصنف اليوم المواقف المختلفة التي قد تتسبب في خروج عملية Node.js، وما يمكننا فعله من خلال الاستماع إلى أحداث خروج العملية هذه.
المبدأ:
عندما تريد عملية ما الخروج، لا يوجد أكثر من حالتين: إحداهما أن العملية تخرج بشكل نشط، والآخر هو أنها تتلقى إشارة نظام تتطلب خروج العملية.
خروج إشعار إشارة النظام
تم إدراج إشارات النظام الشائعة في مستند Node.js الرسمي، ونحن نركز بشكل أساسي على عدد قليل منها:
عند تلقي إشارة خروج غير قسرية، يمكن لعملية Node.js الاستماع إلى إشارة الخروج والقيام ببعض منطق الخروج المخصص. على سبيل المثال، كتبنا أداة cli التي تستغرق وقتًا طويلاً لتنفيذ المهمة. إذا أراد المستخدم الخروج من العملية من خلال ctrl+c قبل اكتمال المهمة، فيمكن مطالبة المستخدم بالانتظار:
const readline = require('. قراءة الخط '); عملية.on('SIGINT', () => { // نستخدم readline لتنفيذ التفاعل في سطر الأوامر ببساطة const rl = readline.createInterface({ الإدخال: العملية.stdin، الإخراج: العملية.stdout }); rl.question('المهمة لم تكتمل بعد، هل أنت متأكد من رغبتك في الخروج؟', الإجابة => { إذا (الإجابة === "نعم") { console.log("تمت مقاطعة تنفيذ المهمة، عملية الخروج"); Process.exit(0); } آخر { console.log("تستمر المهمة..."); } rl.إغلاق(); }); }); // محاكاة مهمة يستغرق تنفيذها دقيقة واحدة const longTimeTask = () => { console.log('بدء المهمة...'); setTimeout(() => { console.log('نهاية المهمة'); }, 1000*60); }; longTimeTask();
التأثير كما يلي: في كل مرة يتم فيها الضغط على ctrl + c، سيُطلب من المستخدم:
تخرج العملية بشكل نشط
من Node.js، وتخرج العملية بشكل نشط، بما في ذلك المواقف التالية بشكل أساسي:
نحن نعلم أن PM2 له تأثير العملية الخفية. في حالتك، عندما تنتهي العملية الخاصة بك مع وجود خطأ، سوف يقوم PM2 بإعادة تشغيل العملية العملية الفرعية في الوضع العنقودي لـ Node.js (في الواقع فإن PM2 له منطق مشابه):
constcluster = require('cluster' ); const http = require('http'); const numCPUs = require('os').cpus().length; عملية ثابتة = تتطلب('عملية'); // رمز العملية الرئيسية إذا (cluster.isMaster) { console.log("ابدأ العملية الرئيسية: ${process.pid}`); // بناءً على عدد مراكز وحدة المعالجة المركزية، قم بإنشاء عملية عمل for (let i = 0; i < numCPUs; i++) { block.fork(); } // استمع إلى حدث خروج العملية المنفذةcluster.on('exit', (worker, code, signal) => { console.log("تم الخروج من العملية المنفذة ${worker.process.pid}، رمز الخطأ: ${code || signal}، إعادة التشغيل...`); // أعد تشغيل العملية الفرعية Clust.fork(); }); } // كود العملية المنفذة if (cluster.isWorker) { // استمع إلى الأحداث التي لم يتم اكتشافها والتي تمت معالجتها بالخطأ.process.on('uncaughtException', error => { console.log("حدث خطأ في العملية المنفذة ${process.pid}`, خطأ); Process.emit('قطع الاتصال'); Process.exit(1); }); // إنشاء خادم الويب // ستستمع كل عملية عاملة إلى المنفذ 8000 (ستتعامل Node.js معه داخليًا ولن تتسبب في تعارض المنافذ) http.createServer((req, res) => { res.writeHead(200); res.end('مرحبا بالعالمn'); }).listen(8000); console.log("بدء العملية المنفذة: ${process.pid}`); }تم تحليل
المواقف
المختلفة التي تخرج فيها عملية Node.js أعلاه. والآن سنقوم بإنشاء أداة لمراقبة خروج العملية، عند خروج عملية Node.js، يُسمح للمستخدم بتنفيذ منطق الخروج الخاص به:
// خطاف الخروج js // احفظ مهام الخروج التي يجب تنفيذها const Tasks = []; // إضافة مهمة خروج const addExitTask = fn => المهام.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))
;
بعد ذلك، نحتاج إلى تنفيذ وظيفة الخروج من العملية الحقيقية، لأن وظيفة المهمة التي يمررها المستخدم قد تكون متزامنة أو غير متزامنة؛ يمكننا استخدام العملية.nextTick للتأكد من تنفيذ رمز المزامنة الخاص بالمستخدم، والتي يمكن فهمها بسهولة. سيتم تنفيذ .nextTick بعد اكتمال تنفيذ التعليمات البرمجية المتزامنة في كل مرحلة من مراحل حلقة الحدث (فهم العملية.nextTick)؛ بالنسبة للمهام غير المتزامنة، نحتاج إلى قيام المستخدم باستدعاء رد الاتصال لإخبارنا بأن المهمة غير المتزامنة قد اكتملت:
// تحديد ما إذا كان إنه قيد الخروج، وتجنب التنفيذ المتعدد لـ Let isExiting = false; const HandleExit = (رمز، خطأ) => { إذا كان (الخروج) يعود؛ isExiting = true; // ضع علامة على أنه تم تنفيذ إجراء الخروج لتجنب الاستدعاءات المتعددة للسماح hasDoExit = fasle; const doExit = () => { إذا عاد (hasDoExit)؛ hasDoExit = true Process.nextTick(() => معالجة.exit(code)) } // سجل عدد المهام غير المتزامنة Let asyncTaskCount = 0; // بعد انتهاء المهمة غير المتزامنة، رد الاتصال الذي يحتاج المستخدم للاتصال به Let ayncTaskCallback = () => { عملية.nextTick(() => { غير متزامنTaskCount-- إذا (asyncTaskCount === 0) doExit() }) } // تنفيذ جميع مهام الخروج Task.forEach(taskFn => { // إذا كان عدد معلمات وظيفة TaskFn أكبر من 1، فسيتم اعتبار أن معلمة رد الاتصال قد تم تمريرها وهي مهمة غير متزامنة if (taskFn.length > 1) { asyncTaskCount++ TaskFn(خطأ، ayncTaskCallback) } آخر { مهمةFn(خطأ) } }); // إذا كانت هناك مهمة غير متزامنة if (asyncTaskCount > 0) { // بعد أكثر من 10 ثوانٍ، قم بالخروج بالقوة setTimeout(() => { doExit(); }، 10 * 1000) } آخر { دو الخروج () } };
عند هذه النقطة، اكتملت أداة مراقبة الخروج من العملية الخاصة بنا، وللتنفيذ الكامل، يمكنك عرض هذه العملية مفتوحة المصدر async-exit-
hook
https://github.com/darukjs/daruk-exit-hook يخرج من
خادم الويب الخاص بنا، عند إعادة التشغيل، أو جدولته بواسطة حاوية قيد التشغيل (pm2 أو عامل الإرساء، وما إلى ذلك)، أو عند حدوث استثناء وخروج العملية، نأمل في تنفيذ إجراءات الخروج، مثل إكمال الاستجابة للطلبات المتصلة بخادم الويب الخاص بنا. الخدمة، وتنظيف اتصال قاعدة البيانات، وطباعة سجلات الأخطاء، وإطلاق الإنذارات، وما إلى ذلك، بعد إكمال إجراء الخروج، ثم الخروج من العملية، يمكننا استخدام أداة مراقبة خروج العملية الآن للتنفيذ:
const http = require(' http'); // إنشاء خادم الويب خادم const = http.createServer((req, res) => { res.writeHead(200); res.end('مرحبا بالعالمn'); }).listen(8000); // استخدم الأداة التي قمنا بتطويرها أعلاه لإضافة مهمة إنهاء العملية addExitTask((error, callback) => { // طباعة سجلات الأخطاء، وإطلاق الإنذارات، وإصدار اتصالات قاعدة البيانات، وما إلى ذلك. console.log('تم خروج العملية بشكل غير طبيعي'، خطأ) // توقف عن قبول الطلبات الجديدة server.إغلاق((خطأ) => { إذا (خطأ) { console.log ("خطأ في إيقاف قبول الطلبات الجديدة"، خطأ) } آخر { console.log("توقف عن قبول الطلبات الجديدة") } }) // الطريقة الأبسط هي الانتظار لفترة زمنية معينة (هنا ننتظر 5 ثوانٍ) حتى تكتمل الطلبات الحالية // إذا كنت تريد التأكد تمامًا من معالجة جميع الطلبات، فأنت بحاجة إلى تسجيل كل اتصال والانتظار حتى تم تحرير كافة الاتصالات، قم بتنفيذ إجراء الخروج // يمكنك الرجوع إلى المكتبة مفتوحة المصدر https://github.com/sebhildebrandt/http-graceful-shutdown setTimout(رد الاتصال، 5 * 1000) })
ملخص
من خلال النص أعلاه، أعتقد أنك على دراية بالمواقف المختلفة التي تتسبب في خروج عملية Node.js. بعد أن تكون الخدمة متصلة بالإنترنت، على الرغم من أن أدوات مثل k8s وpm2 يمكنها سحب العملية بشكل مستمر عند خروج العملية بشكل غير طبيعي لضمان توفر الخدمة، يجب علينا أيضًا أن نشعر بشكل فعال بالخلل أو جدولة العملية في الكود، لذلك لتتمكن من اكتشاف المشاكل مبكرًا.