يعرف الأصدقاء الذين هم على دراية بـ js أن js 是单线程
في العقدة، ويتم اعتماد نموذج خيط واحد متعدد العمليات . نظرًا لقيود JavaScript ذات الخيط الواحد، على الخوادم متعددة النواة، غالبًا ما نحتاج إلى بدء عمليات متعددة لزيادة أداء الخادم إلى الحد الأقصى.
يمكن استخدام مجموعات عمليات Node.js لتشغيل مثيلات Node.js المتعددة، والتي يمكنها توزيع عبء العمل بين سلاسل تطبيقاتها. عندما لا يكون عزل العملية مطلوبًا، استخدم وحدة worker_threads
بدلاً من ذلك، مما يسمح بتشغيل عدة سلاسل عمليات تطبيق داخل مثيل Node.js واحد.
وحدة الكتلة بعد الإصدار V0.8،一个主进程(master) 管理多个子进程(worker) 的方式实现集群
.
تسهل وحدة المجموعة إنشاء عمليات فرعية تشترك في منافذ الخادم.
الطبقة السفلية من الكتلة هي وحدة معالجة الطفل، بالإضافة إلى إرسال الرسائل العادية، يمكنها أيضًا إرسال كائنات أساسية مثل
TCP
وUDP
وما إلى ذلك. وحدةcluster
عبارة عن تطبيق مدمج لوحدةchild_process
ووحدةnet
. عندما تبدأ المجموعة، سيتم تشغيل خادم TCP داخليًا وسيتم إرسال واصف الملف الخاص بمقبس خادم TCP إلى عملية العمل.
في تطبيق وحدة cluster
،一个主进程只能管理一组工作进程
. وضع التشغيل الخاص بها ليس مرنًا مثل وحدة child_process
، ولكنه أكثر استقرارًا:
constcluster = require('cluster') والسمات
.isMaster
العملية الرئيسية،.isPrimary
Node<16.isPrimary العملية الرئيسية،.isWorker
Node>16.isWorker العملية الفرعية. .worker هو.worker
عن مرجع العمل الحالي إلى كائن العملية [في عملية فرعية]..workers
بتخزين تجزئة كائن العملية المنفذة النشط، باستخدام حقل id
كمفتاح. وهذا يجعل من السهل تكرار جميع العمليات المنفذة. إنه متاح فقط في العملية الرئيسية. cluster.wokers[id] === worker
[في العملية الرئيسية].settings
للقراءة فقط، عنصر تكوين المجموعة. بعد استدعاء الأسلوب .setupPrimary() أو .fork()، سيحتوي كائن الإعدادات هذا على الإعدادات، بما في ذلك القيم الافتراضية. سابقا كائن فارغ. لا ينبغي تغيير هذا الكائن أو تعيينه يدويًا.cluster.settings
:- `execArgv` <string[]>قائمة بمعلمات السلسلة التي تم تمريرها إلى الملف القابل للتنفيذ Node.js. **الافتراضي:** `process.execArgv`. - `exec` <string> مسار الملف إلى ملف العملية المنفذة. **الافتراضي:** `process.argv[1]`. - `args` <string[]> تم تمرير وسيطات السلسلة إلى العملية المنفذة. **الافتراضي:** `process.argv.slice(2)`. - `cwd` <string>دليل العمل الحالي للعملية المنفذة. **الافتراضي:** `غير محدد` (موروث من العملية الأصلية). - `التسلسل` <string>يحدد نوع التسلسل المستخدم لإرسال الرسائل بين العمليات. القيم المحتملة هي ""json"" و""متقدمة"". **الافتراضي:** `خطأ`. - `صامت` <boolean>ما إذا كان سيتم إرسال الإخراج إلى الإدخال والإخراج القياسي للعملية الأصلية. **الافتراضي:** `خطأ`. - يقوم `stdio` <Array> بتكوين الإدخال والإخراج القياسي للعملية الناتجة. نظرًا لأن وحدة المجموعة تعتمد على IPC للتشغيل، يجب أن يحتوي هذا التكوين على إدخال ``ipc''. عندما يتم توفير هذا الخيار، فإنه يتجاوز "صامت". - يقوم `uid` <number> بتعيين معرف المستخدم للعملية. - `gid` <number> يعين معرف المجموعة للعملية. - `inspectPort` <number> |. <Function> يضبط منفذ المفتش للعملية المنفذة. يمكن أن يكون هذا رقمًا أو دالة لا تأخذ أي معلمات وتقوم بإرجاع رقم. بشكل افتراضي، يكون لكل عملية عاملة منفذ خاص بها، بدءًا من "process.debugPort" للعملية الرئيسية ومتزايدًا. - `windowsHide` <boolean> إخفاء نافذة وحدة التحكم للعملية التي يتم إنشاؤها عادةً على أنظمة Windows. **الافتراضي:** `خطأ`.
.fork([env])
تولد عملية عاملة جديدة [في العملية الرئيسية].setupPrimary([settings])
Node>16.setupMaster([settings])
يستخدم لتغيير سلوك "fork" الافتراضي، بعد الاستخدام سوف تظهر الإعدادات في cluster.settings
. ستؤثر أي تغييرات في الإعداد فقط على الاستدعاءات المستقبلية إلى .fork()
، وليس العمليات المنفذة بالفعل. تنطبق القيم الافتراضية المذكورة أعلاه على المكالمة الأولى فقط. العقدة أقل من 16 [في العملية الرئيسية].disconnect([callback])
يتم استدعاؤها عندما يتم قطع اتصال كافة العمليات المنفذة وإغلاق المقابض [في العملية الرئيسية]من أجل جعل المجموعة أكثر استقرارًا وقوة، وحدة cluster
أيضًا يعرض العديد من الأحداث:
'message'
، الذي يتم تشغيله عندما تتلقى العملية الرئيسية للمجموعة رسالة من أي عملية عاملة.'exit'
، عندما تموت أي عملية عاملة، ستقوم وحدة المجموعة بتشغيل حدث 'exit'
.cluster.on('exit', (عامل، رمز، إشارة) => { console.log('توفي العامل %d (%s). جارٍ إعادة التشغيل...', عامل.process.pid، رمز الإشارة ||). block.fork(); });
'listening'
، بعد استدعاء listen()
من العملية المنفذة، عند تشغيل حدث 'listening'
على الخادم، ستؤدي cluster
في العملية الرئيسية أيضًا إلى تشغيل حدث 'listening'
.العنقودية ('الاستماع'، (العامل، العنوان) => { console.log( `العامل متصل الآن بـ ${address.address}:${address.port}`); });
'fork'
، عندما يتم إنشاء عملية عاملة جديدة، ستقوم وحدة المجموعة بتشغيل حدث 'fork'
.العنقودية ('شوكة'، (عامل) => { timeouts[worker.id] = setTimeout(errorMsg, 2000); });
'setup'
، الذي يتم تشغيله في كل مرة يتم فيها استدعاء .setupPrimary()
.disconnect
بعد قطع اتصال قناة IPC للعملية المنفذة. عندما تخرج العملية المنفذة بشكل طبيعي، أو يتم إيقافها، أو يتم قطع اتصالها يدويًاالكتلة.on('disconnect', (worker) => { console.log("تم قطع اتصال العامل #${worker.id}`); });
يحتوي كائن Worker
على كافة المعلومات العامة وأساليب العملية المنفذة. في العملية الرئيسية، يمكنك استخدام cluster.workers
للحصول عليه. في العملية المنفذة، يمكنك استخدام cluster.worker
للحصول عليها.
.id
يتم منح id
عملية عاملة جديدة معرفًا فريدًا خاصًا بها. عندما تكون العملية المنفذة نشطة، فهذا هو المفتاح الذي يفهرسها في cluster.workers
..process
يتم إنشاء جميع العمليات المنفذة باستخدام child_process.fork()
، ويتم تخزين الكائن الذي تم إرجاعه بواسطة هذه الوظيفة كـ .process
. في العملية المنفذة، يتم تخزين process
العالمية..send(message[, sendHandle[, options]][, callback])
رسالة إلى العملية المنفذة أو العملية الرئيسية، ويمكنك اختيار استخدام المقبض. في العملية الرئيسية، يرسل هذا رسالة إلى عملية عاملة محددة. إنه نفس ChildProcess.send()
. في العملية المنفذة، يرسل هذا رسالة إلى العملية الرئيسية. إنه نفس process.send()
..destroy()
.kill([signal])
ستقتل هذه الوظيفة العملية المنفذة. تقوم الدالة kill()
بقتل العملية المنفذة دون انتظار قطع الاتصال بسلاسة، ولها نفس سلوك worker.process.kill()
. للتوافق مع الإصدارات السابقة، يُطلق على هذا التابع اسم مستعار لـ worker.destroy()
..disconnect([callback])
إلى العملية المنفذة، مما يؤدي إلى استدعاء .disconnect()
الخاص بها والذي سيؤدي إلى إيقاف تشغيل جميع الخوادم، وانتظار أحداث 'close'
على تلك الخوادم، ثم قطع اتصال قناة IPC..isConnect()
ترجع هذه الدالة true
إذا كانت العملية المنفذة متصلة بعمليتها الرئيسية من خلال قناة IPC الخاصة بها، false
بخلاف ذلك. تتصل العمليات المنفذة بالعملية الرئيسية الخاصة بها بعد الإنشاء..isDead()
ترجع هذه الدالة true
إذا تم إنهاء العملية المنفذة (بسبب الخروج أو استلام الإشارة). وإلا فإنها ترجع false
.من أجل جعل المجموعة أكثر استقرارًا وقوة، تعرض وحدة cluster
أيضًا العديد من الأحداث:
'message'
، في العملية المنفذة.'exit'
'exit'
当前worker工作进程
[id].on('message', messageHandler);
إذا (cluster.isPrimary) { عامل ثابت = block.fork(); Worker.on('exit', (رمز، إشارة) => { إذا (إشارة) { console.log("مقتل العامل بالإشارة: ${signal}`); } وإلا إذا (الكود !== 0) { console.log("خرج العامل برمز الخطأ: ${code}`); } آخر { console.log("نجاح العمال!"); } }); }
'listening'
، قم باستدعاء listen()
من العملية المنفذة للاستماع إلى العملية المنفذة الحالية.cluster.fork().on('listening', (address) => { // العملية المنفذة تستمع });
disconnect
، والذي يتم تشغيله بعد قطع اتصال قناة IPC للعملية المنفذة. عندما تخرج العملية المنفذة بشكل طبيعي، أو يتم إيقافها، أو يتم قطع اتصالها يدويًا بنظامcluster.fork().on('disconnect', () => { // يقتصر على التشغيل على كائن العامل الحالي});
في العقدة، يتم استخدام الاتصال بين العمليات (IPC) لتحقيق الاتصال بين العمليات بين العملية الرئيسية والعمليات الفرعية طريقة .send()
(a.send تعني إرسال رسالة إلى إرسال) لإرسال الرسائل والاستماع إلى أحداث message
لجمع المعلومات. يتم تنفيذ ذلك بواسطة cluster模块
من خلال دمج EventEmitter
. وهو أيضًا مثال بسيط للاتصال بين العمليات على الموقع الرسمي
process.on('message')
, process.send()
child.on('message')
, child.send()
# الكتلة.isMaster # الكتلة. شوكة () # كتلة العمال #cluster.workers[id].on('message', messageHandler); # block.workers[id].send(); # عملية.on('message', messageHandler); # عملية.إرسال ()؛ كتلة ثابتة = تتطلب('الكتلة'); const http = require('http'); # العملية الرئيسية إذا (cluster.isMaster) { // تتبع طلبات http console.log("العملية ${process.pid} الأساسية قيد التشغيل`); دع numReqs = 0؛ // عد الطلبات وظيفة messageHandler(msg) { إذا (msg.cmd && msg.cmd === 'notifyRequest') { numReqs += 1; } } // ابدأ العمال واستمع إلى الرسائل التي تحتوي على notifyRequest // بدء عملية متعددة (عدد نوى وحدة المعالجة المركزية) // عملية تفرخ العامل. const numCPUs = require('os').cpus().length; لـ (دع i = 0؛ i < numCPUs؛ i++) { console.log(i) block.fork(); } // تتواصل العملية الرئيسية لعامل المجموعة مع العمليات الفرعية for (const id inclus.workers) { // *** استمع إلى الأحداث من العمليات الفرعيةcluster.workers[id].on('message', messageHandler); // *** أرسل الكتلة.workers[id].send({ إلى العملية الفرعية النوع: "masterToWorker"، من: "سيد"، بيانات: { الرقم: Math.floor(Math.random() * 50) } }); } cluster.on('exit', (عامل، كود، إشارة) => { console.log(`مات العامل ${worker.process.pid}`); }); } آخر { # العمليات الفرعية // يمكن للعمليات العاملة مشاركة أي اتصال TCP // في هذا المثال، هو خادم HTTP // العمليات العاملة لديها خادم http. http.Server((req, res) => { res.writeHead(200); res.end('مرحبا بالعالمn'); //******! ! ! ! إخطار سيد حول الطلب! ! ! ! ! ! ******* //****** إرسال العملية.send({ cmd: 'notifyRequest' }); //****** استمع إلى العملية ('message'، function(message) { // كسكسكسكسكسكس }) }).listen(8000); console.log("بدأ العامل ${process.pid}`); }
يتضمن الاتصال بين عمليات NodeJS تمرير الرسائل فقط، ولا ينقل الكائنات فعليًا.
قبل إرسال الرسالة، ستقوم طريقة send()
بتجميع الرسالة في مقبض ورسالة، وسيتم إجراء تسلسل لهذه الرسالة بواسطة JSON.stringify
، وهذا يعني أنه لن يتم تمرير الكائن بأكمله يتم إرسالها عبر قناة IPC، وهي عبارة عن سلاسل ويتم استعادتها إلى الكائنات من خلال JSON.parse
بعد الإرسال.
يوجد app.listen(port)
في الكود عند التفرع، لماذا يمكن لعمليات متعددة الاستماع إلى نفس المنفذ؟
والسبب هو أن العملية الرئيسية ترسل مقبض كائن الخدمة الذي ينتمي إلى العملية الرئيسية إلى عمليات فرعية متعددة من خلال طريقة الإرسال ()، لذلك لكل عملية فرعية، بعد استعادة المقبض، تحصل على نفس كائن الخدمة. عندما يتم تقديم طلب إلى الخادم على الشبكة، تكون خدمة العملية وقائية، لذلك لن يحدث أي استثناء عند الاستماع على نفس المنفذ.
# master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); for (let i=0; i<cpus.length; i++) { عامل ثابت = شوكة('worker.js'); console.log('تم إنشاء عملية العامل، معرف المنتج: %s ppid: %s'، عامل.pid، معالجة.pid)؛ }
#worker.js const http = require('http'); http.createServer((req, res) => { res.end('أنا عامل، معرف المنتج: '+process.pid +'، ppid:' +process.ppid); }).listen(3000);
في مثال التعليمات البرمجية أعلاه، عندما تقوم وحدة التحكم بتنفيذ
node master.js
يمكن لعامل واحد فقط الاستماع إلى المنفذ 3000، وسيقوم الباقي بإلقاءError: listen EADDRINUSE :::3000
خطأ.
发送句柄
بين العمليات بعد الإصدار v0.5.9/** * http://nodejs.cn/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback * رسالة * إرسال المقبض */ subprocess.send(message, sendHandle)
بعد إنشاء قناة IPC بين العمليات الأصلية والتابعة، يتم إرسال الرسالة من خلال طريقة الإرسال لكائن العملية الفرعية.二个参数sendHandle 就是句柄,可以是TCP套接字、TCP服务器、UDP套接字等
من أجل حل مشكلة احتلال المنفذ متعدد العمليات أعلاه، نقوم بتمرير مقبس العملية الرئيسية إلى العملية الفرعية.
#master.js const fork = require('child_process').fork; const cpus = require('os').cpus(); خادم const = require('net').createServer(); server.listen(3000); معالجة.العنوان = "العقدة الرئيسية" for (let i=0; i<cpus.length; i++) { عامل ثابت = شوكة('worker.js'); # مرر المقبض عامل.send('server', server); console.log('تم إنشاء عملية العامل، معرف المنتج: %s ppid: %s'، عامل.pid، معالجة.pid)؛ }
// عامل.js دع العامل؛ Process.title = "عامل العقدة" عملية.on('message', وظيفة (message, sendHandle) { إذا (الرسالة === 'الخادم') { عامل = إرسالهاندلي؛ عامل.ون ("اتصال"، وظيفة (مقبس) { console.log('أنا عامل، معرف المنتج: '+process.pid +'، ppid:' +process.ppid) }); } });
تحقق من أن وحدة التحكم تنفذ node master.js
إذا فهمت cluster
، ستعرف أن العمليات الفرعية يتم إنشاؤها من خلال cluster.fork()
. في Linux، يوفر النظام أصلاً طريقة fork
، فلماذا تختار Node تنفيذ cluster模块
بنفسها بدلاً من استخدام الطريقة الأصلية للنظام مباشرة؟ الأسباب الرئيسية هي النقطتين التاليتين:
تراقب عملية الشوكة نفس المنفذ، مما قد يتسبب في حدوث أخطاء في احتلال المنفذ،
ولا يوجد موازنة تحميل بين عمليات الشوكة، مما قد يؤدي بسهولة إلى ظاهرة القطيع الهادر
في cluster模块
المشكلة الأولى هي تحديد ما إذا كانت العملية الحالية هي master进程
، وإذا كانت كذلك، فإنها تستمع على المنفذ. وإذا لم تكن كذلك، فسيتم تمثيلها worker进程
شوكية ولا تستمع على المنفذ.
ردًا على السؤال الثاني، تحتوي cluster模块
على وظيفة موازنة التحميل المضمنة، وتكون master进程
مسؤولة عن الاستماع إلى المنفذ لتلقي الطلبات، ثم تخصيصها worker进程
المقابلة من خلال خوارزمية الجدولة (الإعداد الافتراضي هو Round-Robin، يمكن تعديل خوارزمية الجدولة من خلال متغير البيئة NODE_CLUSTER_SCHED_POLICY
).
عندما يطرح الكود استثناءً لم يتم اكتشافه، سيتم إنهاء العملية في هذا الوقت، يوفر Node.js واجهة process.on('uncaughtException', handler)
لالتقاطه، ولكن عندما عندما تواجه العملية العاملة استثناءً لم يتم اكتشافه، فهي بالفعل في حالة غير مؤكدة في هذا الوقت، يجب أن نسمح للعملية بالخروج بأمان:
+---------+ +--------+ | +---------+ +----+----+ | +----------------+ | | |.<---------+ | |.+----+----+ |. افصل |. شوكة عامل جديد | +-------------------------> + ----------------------- -> | |.انتظر... | | +------------------------> | | يموت | | |
عندما يكون لدى العملية استثناء يسبب تعطلًا أو OOM ويقتلها النظام، على عكس حدوث استثناء لم يتم اكتشافه، لا يزال لدينا فرصة للسماح للعملية بمواصلة التنفيذ تخرج العملية الحالية مباشرة، ويقوم السيد على الفور بتقسيم عامل جديد.
توفر وحدة معالجة الطفل القدرة على استخلاص العمليات الفرعية، وهي ببساطة执行cmd命令的能力
. افتراضيًا، stdin、 stdout 和stderr 的管道会在父Node.js 进程和衍生的子进程之间建立
. تتمتع خطوط الأنابيب هذه بقدرة محدودة (وخاصة بالمنصة). إذا تجاوزت العملية الفرعية هذا الحد عند الكتابة إلى stdout ولم يتم التقاط أي إخراج، فستحظر العملية الفرعية وتنتظر حتى يقبل المخزن المؤقت للأنبوب المزيد من البيانات. هذا هو نفس سلوك الأنبوب الموجود في الغلاف. إذا لم يتم استهلاك الإخراج، استخدم الخيار { stdio: 'ignore' }.
const cp = require('child_process');
ليس لدى العملية الفرعية التي تم إنشاؤها من خلال واجهة برمجة التطبيقات أي اتصال ضروري بالعملية الأصلية.
يتم استخدام 4 طرق غير متزامنة لإنشاء عمليات فرعية: fork، exec، execFile، Spawn
Node.
fork(modulePath, args)
: يُستخدم عندما تريد تشغيل عملية Node كعملية مستقلة، بحيث يتم فصل معالجة الحساب وموصف الملف عن عملية Node الرئيسية (نسخ عملية فرعية)Non-Node
spawn(command, args)
: معالجة بعض المشكلات استخدم execFile(file, args[, callback]) عندما يكون هناك العديد من عمليات الإدخال/الإخراج الفرعية أو عندما تحتوي العملية على قدر كبير من الإخراجexecFile(file, args[, callback])
استخدمه عندما تحتاج فقط إلى تنفيذ برنامج خارجي سرعة التنفيذ سريعة وآمنة نسبيًا لمعالجة إدخال المستخدم.exec(command, options)
: يُستخدم عندما تريد الوصول مباشرة إلى أمر Shell الخاص بمؤشر الترابط، تأكد من الانتباه إلىطرق المزامنة الثلاثة التي أدخلها المستخدم: execSync
، execFileSync
، spawnSync
الطرق الثلاثة الأخرى هي امتدادات لـ spawn()
.
. تذكر أن عملية Node.js الفرعية مستقلة عن العملية الأصلية، باستثناء قناة اتصال IPC المنشأة بين الاثنين. كل عملية لها ذاكرتها الخاصة ومثيل V8 الخاص بها
.
على سبيل المثال، قم بإنشاء ملفين،worker.js وmaster.js، في الدليل:
#child.js const t = JSON.parse(process.argv[2]); console.error(`عملية فرعية t=${JSON.stringify(t)}`); Process.send({hello:`son pid=${process.pid} يرجى إعطاء أبي معرف العملية=${process.ppid} hello`}); عملية.on('رسالة', (msg)=>{ console.error(`العملية الفرعية msg=${JSON.stringify(msg)}`); });
#parent.js const {fork} = require('child_process'); ل(دع أنا = 0; أنا < 3; i++){ const p = fork('./child.js', [JSON.stringify({id:1,name:1})]); p.on('message', (msg) => { console.log(`messsgae from Child msg=${JSON.stringify(msg)}`, ); }); p.send({hello:`تحية من أبي ${process.pid} معرف العملية=${i}`}); }
ابدأ تشغيلparent.js من خلال node parent.js
، ثم تحقق من عدد العمليات من خلال ps aux | grep worker.js
يمكننا أن نجد أنه من الناحية المثالية، عدد العمليات يساوي عدد نوى وحدة المعالجة المركزية، وكل عملية تستخدم واحدًا وحدة المعالجة المركزية الأساسية.
هذا هو وضع العامل الرئيسي الكلاسيكي (وضع السيد والعبد)
في الواقع، يعد تفرع العملية أمرًا مكلفًا، والغرض من نسخ العملية هو الاستفادة الكاملة من موارد وحدة المعالجة المركزية، لذلك يستخدم NodeJS نهجًا يحركه الحدث في مؤشر ترابط واحد لحل مشكلة التزامن العالي.
السيناريوهات القابلة للتطبيق <br/> تُستخدم بشكل عام للسيناريوهات التي تستغرق وقتًا طويلاً، ويتم تنفيذها باستخدام العقدة، مثل تنزيل الملفات؛
يمكن لـ Fork تنفيذ التنزيل متعدد الخيوط: تقسيم الملف إلى كتل متعددة، ثم تقوم كل عملية بتنزيل جزء منه، ثم تجميعها أخيرًا؛
const cp = require('child_process'); // المعلمة الأولى هي اسم أو مسار الملف القابل للتنفيذ المراد تشغيله. هنا صدى cp.execFile('echo', ['hello', 'world'], (err, stdout, stderr) => { إذا (يخطئ) {console.error(err); console.log('stdout:', stdout); console.log('stderr:', stderr); });
السيناريوهات القابلة للتطبيق <br/> أكثر ملاءمة للمهام ذات الحمل المنخفض والمزيد من الاهتمام بالنتائج، مثل ls، وما إلى ذلك؛
يستخدم بشكل أساسي لتنفيذ طريقة shell، ويتم ذلك لا يزال يتم استدعاؤه داخليًا، ولكن يوجد حد أقصى لذاكرة التخزين المؤقت.
const cp = require('child_process'); cp.exec(`cat ${__dirname}/messy.txt | فرز | uniq`, (err, stdout, stderr) => { console.log(stdout); });
السيناريوهات القابلة للتطبيق <br/>أكثر ملاءمة للمهام ذات الحمل المنخفض وإيلاء المزيد من الاهتمام للنتائج، مثل ls، وما إلى ذلك؛
مهمة واحدة
لواجهة دفق الإدخال/الإخراجconst cp = require('child_process'); const Child = cp.spawn('echo', ['hello', 'world']); Child.on('خطأ', console.error); # الإخراج هو دفق، والإخراج إلى العملية الرئيسية stdout، وحدة التحكم Child.stdout.pipe(process.stdout); Child.stderr.pipe(process.stderr)
تسلسل متعدد المهام
const cp = require('child_process'); مسار ثابت = يتطلب('مسار'); const cat = cp.spawn('cat', [path.resolve(__dirname, 'messy.txt')]); constsort = cp.spawn('sort'); const uniq = cp.spawn('uniq'); # الإخراج هو دفق cat.stdout.pipe(sort.stdin); sort.stdout.pipe(uniq.stdin); uniq.stdout.pipe(process.stdout)
السيناريوهات القابلة للتطبيق
يتم بث البث، لذا فهو مناسب للمهام التي تستغرق وقتًا طويلاً، مثل تنفيذ تثبيت npm وطباعة عملية التثبيت
يتم تشغيل الإغلاق بعد انتهاء العملية ودفق الإدخال والإخراج القياسي (sdtio) لـ تم 'close'
العملية الفرعية. يختلف هذا الحدث عن exit
لأنه يمكن لعمليات متعددة مشاركة نفس دفق stdio.
المعلمات:
سؤال: هل يجب أن يكون الكود موجودًا؟
(يبدو أنه ليس من التعليقات على الكود) على سبيل المثال، إذا كنت تستخدم kill
لقتل العملية الفرعية، فما هو الكود؟
معلمات الخروج:
رمز، إشارة، إذا خرجت العملية الفرعية من تلقاء نفسها، فإن code
هو رمز الخروج، وإلا فهو فارغ؛
إذا تم إنهاء العملية الفرعية من خلال إشارة، فإن signal
هي الإشارة لإنهاء العملية، وإلا فهي فارغة.
ولا يجوز أن يكون أحدهما باطلا.
أشياء يجب ملاحظتها :
عند تشغيل حدث exit
، قد يظل دفق stdio للعملية الفرعية مفتوحًا. (السيناريو؟) بالإضافة إلى ذلك، تستمع العقدة إلى إشارات SIGINT وSIGTERM، وهذا يعني أنه عندما تتلقى العقدة هاتين الإشارتين، فإنها لن تخرج على الفور، وبدلاً من ذلك، ستقوم ببعض أعمال التنظيف أولاً ثم تعيد طرح هاتين الإشارتين. (من الناحية المرئية، يمكن لـ js القيام بأعمال التنظيف في هذا الوقت، مثل إغلاق قاعدة البيانات، وما إلى ذلك.)
SIGINT
: مقاطعة، إشارة إنهاء البرنامج، يتم إصدارها عادةً عندما يضغط المستخدم على CTRL + C، وتستخدم لإعلام العملية الأمامية بإنهاء العملية.
SIGTERM
: إنهاء، إشارة نهاية البرنامج، يمكن حظر هذه الإشارة ومعالجتها، وعادة ما تستخدم لمطالبة البرنامج بالخروج بشكل طبيعي. يقوم أمر shell kill بإنشاء هذه الإشارة بشكل افتراضي. إذا تعذر إنهاء الإشارة، فسنحاول SIGKILL (الإنهاء القسري).
عندما تحدث الأمور التالية، سيتم تشغيل الخطأ. عند حدوث خطأ، قد يتم تشغيل الخروج أو لا يتم تشغيله. (القلب مكسور)
عند استخدام process.send()
لإرسال رسالة.
المعلمة :
message
هي كائن json، أو قيمة أولية؛ sendHandle
، أو كائن net.Socket، أو كائن net.Server (يجب أن يكون الطلاب المطلعون على المجموعة على دراية بهذا)
: عند استدعاء .disconnected()
، قم بتعيينه إلى كاذبة. يمثل ما إذا كان يمكنه تلقي رسائل من العملية الفرعية أو إرسال رسائل إلى العملية الفرعية.
.disconnect() : أغلق قناة IPC بين العملية الأصلية والعملية الفرعية. عند استدعاء هذه الطريقة، سيتم إطلاق حدث disconnect
. إذا كانت العملية الفرعية عبارة عن مثيل عقدة (تم إنشاؤها من خلال Child_process.fork())، فيمكن أيضًا استدعاء العملية الفرعية process.disconnect()
بشكل نشط داخل العملية الفرعية لإنهاء قناة IPC.
للمشكلات ذات الخيوط الفردية عادةً لمحاكاة الخيوط المتعددة
تشغل
عملية
البرمجية单线程
، لكن البيئة المضيفة لجافا سكريبت، سواء كانت Node أو المتصفح، متعددة الخيوط.
لماذا جافا سكريبت ذات ترابط واحد؟
يجب أن تبدأ هذه المشكلة بالمتصفح. بالنسبة لعمليات DOM في بيئة المتصفح، تخيل فقط أنه إذا كانت هناك سلاسل عمليات متعددة تعمل على نفس DOM، فسيكون ذلك فوضويًا. وهذا يعني أنه لا يمكن إجراء عمليات DOM إلا بطريقة واحدة تجنب تعارضات عرض DOM. في بيئة المتصفح، يكون مؤشر ترابط عرض واجهة المستخدم ومحرك تنفيذ JS متعارضين. عند تنفيذ أحدهما، سيتم تعليق الآخر. يتم تحديد ذلك بواسطة محرك JS.
process.env.UV_THREADPOOL_SIZE.= 644.
worker_threads
لتوفير إمكانات حقيقية متعددة مؤشرات الترابط للعقدة.const { هوالموضوع الرئيسي, ميناء الوالدين, بيانات العمال, معرف الخيط, قناة الرسائل, منفذ رسالة, عامل } = require('worker_threads'); الوظيفة الرئيسية () { لـ (دع i = 0; i < 5; i++) { const عامل = عامل جديد(__filename, {workerData: i }); worker.on('exit', code => { console.log(`main: توقف العامل برمز الخروج ${code}`); }); عامل.ون('رسالة', رسالة => { console.log(`الرئيسي: تلقي ${msg}`); عامل.postMessage(msg + 1); }); } } وظيفة عامل الموضوع () { console.log(`worker:workerDate ${workerData}`); parentPort.on('message', msg => { console.log("العامل: تلقي ${msg}`); })، parentPort.postMessage(workerData); } إذا (هوالموضوع الرئيسي) { mainThread(); } آخر { عامل الموضوع(); }
const Accept = require('assert'); ثابت { عامل, قناة الرسائل, منفذ رسالة, هوالموضوع الرئيسي, parentPort } = require('worker_threads'); إذا (هوالموضوع الرئيسي) { عامل ثابت = عامل جديد(__اسم الملف); const subChannel = new messageChannel(); عامل.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('message', (value) => { console.log('تم الاستلام:', قيمة); }); } آخر { parentPort.once('message', (value) => { تأكيد(value.hereIsYourPort مثيل messagePort); value.hereIsYourPort.postMessage('العامل يرسل هذا'); value.hereIsYourPort. Close(); }); }
هي أصغر وحدة لتخصيص الموارد، والخيط هو أصغر وحدة لجدولة وحدة المعالجة المركزية.
IPC (الاتصال بين العمليات) هو进程间通信
نظرًا لأن كل عملية لها مساحة عنوان مستقلة خاصة بها بعد الإنشاء، فإن الغرض من تنفيذ IPC هو مشاركة الوصول إلى الموارد بين العمليات.
هناك العديد من الطرق لتنفيذ IPC: يتم تنفيذ الأنابيب وقوائم انتظار الرسائل والإشارات ومآخذ المجال وNode.js من خلال الأنابيب.
في الواقع، ستقوم العملية الأصلية أولاً بإنشاء قناة IPC والاستماع إلى IPC قبل إنشاء العملية الفرعية، ثم إنشاء العملية الفرعية، وستخبر العملية الفرعية واصف الملف المتعلق بقناة IPC من خلال متغير البيئة (. NODE_CHANNEL_FD). تبدأ العملية الفرعية في هذا الوقت، يتم توصيل قناة IPC وفقًا لواصف الملف لإنشاء اتصال بالعملية الأصلية.
المقبض هو مرجع يمكن استخدامه لتحديد المورد، وهو يحتوي على واصف مورد الملف الذي يشير إلى الكائن.
بشكل عام، عندما نريد مراقبة عمليات متعددة على منفذ واحد، قد نفكر في استخدام وكيل العملية الرئيسي:
ومع ذلك، سيؤدي حل الوكيل هذا إلى استخدام واصفين للملفات عند استقبال كل طلب وإعادة توجيه الوكيل، وستكون واصفات ملفات النظام محدودة.
فلماذا استخدام المقابض؟ والسبب هو أنه في سيناريوهات التطبيق الفعلية، قد يتضمن إنشاء اتصال IPC سيناريوهات معالجة بيانات أكثر تعقيدًا، ويمكن تمرير المقبض كمعلمة اختيارية ثانية لأسلوب send()
، مما يعني أنه يمكن تمرير معرف المورد مباشرة يتجنب النقل استخدام واصفات الملفات الناتجة عن إعادة توجيه الوكيل المذكور أعلاه.
فيما يلي أنواع المقبض التي تدعم الإرسال:
بعد أن تقوم العملية الأصلية للعملية المعزولة بإنشاء عملية فرعية، يتم إنهاء العملية الأصلية، ولكن عملية فرعية واحدة أو أكثر لا تزال العمليات المقابلة لعملية الوالدين على قيد الحياة. يتضح ذلك من خلال مثال الكود التالي.
# العامل const http = require ('http') ؛ const server = http.createserver ((req ، res) => { res.end ('أنا عامل ، pid:' + process.pid + '، ppid:' + process.ppid) ؛ // سجل عملية العامل الحالية PID وعملية الوالدين PPID }); دع العامل Process.on ('message' ، function (message ، sendhandle) { if (message === 'server') { عامل = sendhandle ؛ العامل. server.emit ('connection' ، socket) ؛ }); }
}
) ؛
const fork = require ('child_process'). fork ؛ const server = required ('net'). correadEserver () ؛ server.listen (3000) ؛ عامل const = شوكة ('worker.js') ؛ Worker.send ('server' ، server) ؛ console.log ('عملية العمال التي تم إنشاؤها ، PID: ٪ s ppid: ٪ s' ، worker.pid ، process.pid) ؛ process.exit (0) ؛// بعد إنشاء عملية الطفل
،
ستصبح العملية الرئيسية.
منذ خروج عملية الأصل في Master.js ، يوضح مراقبة النشاط عملية العمال فقط.
تحقق مرة أخرى ، افتح واجهة استدعاء وحدة التحكم ، يمكنك أن ترى أن PPID المقابلة لعملية العامل 5611 هي 1 (لعملية البداية) ، وأصبحت عملية يتيمة في هذا الوقت.
الخفي تعمل في الخلفية ولا تتأثر بالمحطة.
node app.js
يكون الطلاب الذين يقومون بتطوير Node.js على دراية به.前台运行模式
.
إذا تم استخدام طريقة عملية Daemon ، بعد تنفيذ node app.js
لبدء عملية خدمة في هذه المحطة ، يمكنني أيضًا القيام بأشياء أخرى في هذه المحطة دون التأثير على بعضها البعض.
إنشاء عملية طفل
الطفل
(استدعاء وظيفة النظام)
قم بتغيير دليل العمل لعملية الطفل (مثل: "/"
options.detached
ستستدعي طبقة SETSID طريقةالخطوة
const تفرخ = مطلوب ('child_process'). تفرخ ؛ وظيفة startDaemon () { Const Daemon = Spawn ('Node' ، ['daemon.js'] ، { CWD: '/usr' ، منفصل: صحيح ، stdio: "تجاهل" ، }); console.log ('Daemon Process تبدأ عملية الوالدين PID: ٪ S ، عملية Daemon PID: ٪ s' ، process.pid ، daemon.pid) ؛ Daemon.Unref () ؛ } startDaemon ()
يبدأ
منطق المعالجة في ملف Daemon.js مؤقتًا وينفذه كل 10 ثوانٍ حتى لا يخرج هذا المورد.
من عملية الطفل.const fs = require('fs'); const {console} = require ('console') ؛ // مسجل بسيط مخصص const logger = وحدة تحكم جديدة (fs.createwRiteStream ('./ stdout.log') ، fs.createwRiteReam ('./ stderr.log')) ؛ setInterval (function () { logger.log ('daemon pid:' ، process.pid ، '، ppid:' ، process.ppid) ؛ } ، 1000 * 10
)
؛
في العمل الفعلي ، نحن لسنا غرباء لعمليات الخفيون ، مثل PM2 ، وكتلة البيض ، إلخ. لا تزال عملية الخفي مرتفعة للغاية.
5.ما هو
process.cwd()
من دليل الأصل ، والتي يمكن الحصول عليها من خلال إعادة تعيين أمر process.chdir()
ماذا
تفعل؟
لن يتم الحصول على النتيجة الصحيحة. في حالة أخرى ، يتم البحث عن وحدة الطرف الثالث المشار إليها في البرنامج بناءً على الدليل الذي تبدأ فيه العملية الحالية.
// مثال على ذلك