العناقيد في Node.js

من موسوعة حسوب
< Node.js
مراجعة 11:16، 23 أكتوبر 2018 بواسطة عبد اللطيف ايمش (نقاش | مساهمات) (استبدال النص - '\[\[تصنيف:(.*)\]\]' ب'{{SUBPAGENAME}}')
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)
اذهب إلى التنقل اذهب إلى البحث

الاستقرار: 2-مستقر

تعمل نسخةٌ واحدةٌ من Node.js في خيط (thread) واحد. للاستفادة من الأنظمة ذات الأنوية المتعدِّدة (multi-core)، سيضطر المستخدم حينها إلى تشغيل عنقود من عمليات Node.js لمعالجة الحمل.

تسمح الوحدة cluster بإنشاء عمليات أبناء بسهولة تتشارك جميعها منافذ الخادم نفسها.

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // (worker) تفريع مسار العمليات العاملة
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  // TCP تستطيع العمليات أن تتشارك أي اتصال
  // http في هذه الحالة، الخادم هو خادم
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);

  console.log(`Worker ${process.pid} started`);
}

ستشارك Node.js التي تعمل الآن المنفذ 8000 بين العمليات العاملة (workers):

$ node server.js
Master 3596 is running
Worker 4324 started
Worker 4520 started
Worker 6056 started
Worker 5644 started

انتبه رجاءً إلى أنَّه ليس بالإمكان إعداد أنبوب مسمى (named pipe) لخادم في عمليَّة عاملة في بيئة ويندوز.

كيفية العمل

تُولَّد العمليات العاملة باستعمال التابع child_process.fork()‎، لذا بإمكان هذه العمليات التواصل مع العمليات الأب لها عبر قناة الاتصال IPC وتمرير معالجات الخادم ذهابًا وإيابًا.

تدعم وحدة العناقيد (cluster) أسلوبين لتوزيع الاتصالات الواردة؛ أولهما (ويعدُّ الاسلوب الافتراضي في جميع المنصات باستثناء ويندوز) يعتمد على التناوب (round-robin)، إذ تستمع العمليَّة الرئيسية (master process) إلى المنفذ، وتقبل الاتصالات الجديدة، ثمَّ تُوزِّعها بطريقة التناوب مع وجود بعض الذكاء المدمج لمنع التحميل الزائد لعمليَّة عاملة. أمَّا أسلوب التابع الثاني فيعتمد على إنشاء العمليَّة الرئيسية مقبس استماع ثمَّ إرساله إلى العمليَّة العاملة المعنيَّة؛ تقبل بعدئذٍ العمليَّة العاملة الاتصالات الواردة مباشرةً.

نظريًّا، يجب أن يعطي الأسلوب الثاني أداءً أفضل. أمَّا عمليًّا، وُجِد أنَّ أسلوب التوزيع يفقد توازنه نتيجة حدوث تقلبات في مُجَدوِل نظام التشغيل، إذ تمت مراقبة أحمال معيَّنة ولوحظ أنَّ 70% من جميع الاتصالات قد وُزِّعت على عمليتين فقط من أصل ثمان عمليات!

لمَّا كان التابع server.listen()‎ يُسنِد غالب العمل إلى العمليَّة الرئيسية، نتج لدينا ثلاثة حالات يختلف فيها سلوك عمليَّة Node.js الطبيعية عن سلوك العمليَّة العاملة في عنقود وهي:

  1. server.listen({fd: 7})‎: لمَّا كانت الرسائل تُمرَّر إلى العمليَّة الرئيسية، فسيكون واصف الملف 7 في العمليَّة الأب مستمعًا، وسيُمرَّر المعالج إلى العمليَّة العاملة بدلًا من الاستماع إلى تخمين العمليَّة العاملة حول موارد واصف الملف ذي الرقم 7 المحتملة.
  2. server.listen(handle)‎: سيُسبِّب الاستماع إلى المعالجات بشكل صريح بجعل العمليَّة العاملة تستعمل المعامل المعطى بدلًا من التحدث إلى العمليَّة الرئيسية.
  3. server.listen(0)‎: هذا سيجعل الخادم يستمع إلى منفذٍ عشوائيٍّ. مع ذلك، ستستلم كل عمليَّة عاملة في عنقود نفس المنفذ العشوائي في كل مرة يُستدعَى فيها listen(0)‎. الخلاصة، يكون المنفذ عشوائيًّا في المرة الأولى فقط ويمكن بعدها تخمين المنفذ. لاستعمال منفذ فريد، يمكن توليد رقم المنفذ اعتمادًا على معرف (ID) العمليَّة العاملة في العنقود.

لا توفِّر Node.js التوجيه المنطقي (routing logic). بناءً على ذلك، من المهم تصميم تطبيقٍ لا يعتمد بشكل كبير على الكائنات التي تخزِّن بياناتها في الذاكرة من أجل بعض الأمور مثل الجلسات وتسجيل الدخول.

بما أنَّ العمليات العاملة هي عمليات منفصلة تمامًا، يمكن قتلها أو إعادة توليدها بناءً على حاجة التطبيق دون التأثير على أيَّة عمليَّة عاملة أخرى. يستمر الخادم بقبول الاتصالات طالما هنالك بعض عمليات عاملة ما زالت على قيد الحياة. إن لم يعد هنالك أيَّة عمليَّة عاملة تعمل، فستنهى الاتصالات الموجودة آنذاك وستُرفض أيُّة اتصالات جديدة. لا تدير Node.js عدد العمليات العاملة تلقائيًّا، إذ يُعدُّ هذا من مسؤوليات التطبيق ليدير مجمِّع العمليَّة العاملة بقدر ما يحتاج.

رغم أنَّ الهدف الرئيسي من الوحدة cluster هو استعمالها في الشبكات إلا أنَّه يمكن استعمالها في حالات أخرى حيث تتطلب عمليات عاملة.

الصنف Worker

أضيف في الإصدار v0.7.0.

يحوي الكائن Worker على جميع المعلومات والتوابع العامة المتعلقة بالعمليات العاملة (worker). في حالة عمليَّة رئيسية (master)، يمكن الحصول عليه باستعمال cluster.workers. أمَّا في حالة عمليَّة عاملة (worker)، يمكن الحصول عليه باستعمال cluster.worker.

الحدث 'disconnect'

أضيف في الإصدار v0.7.7.

يشبه هذا الحدثُ الحدثَ cluster.on('disconnect')‎ باستثناء أنَّه يمكن استعماله بشكل خاص مع الصنف Worker.

cluster.fork().on('disconnect', () => {
  // فُصِلَت العمليَّة العاملة الآن
});

الحدث 'error'

أضيف في الإصدار v0.7.3.

هذا الحدث مماثل تمامًا لذلك الذي يوفره التابع child_process.fork()‎.

يمكن استدعاء process.on('error')‎ أحيانًا ضمن العمليَّة العاملة.

الحدث 'exit'

أضيف في الإصدار v0.11.2.

  • code: ‏<number> حالة الخروج إن تم الخروج من العمليَّة بشكل طبيعي.
  • signal: ‏<string> اسم الإشارة التي تسبَّبت في قتل أو إنهاء العمليَّة، مثل الإشارة SIGHUP.

يشبه هذا الحدثُ الحدثَ cluster.on('exit')‎ باستثناء أنَّه يمكن استعماله بشكل خاص مع الصنف Worker.

const worker = cluster.fork();
worker.on('exit', (code, signal) => {
  if (signal) {
    console.log(`worker was killed by signal: ${signal}`);
  } else if (code !== 0) {
    console.log(`worker exited with error code: ${code}`);
  } else {
    console.log('worker success!');
  }
});

الحدث 'listening'

أضيف في الإصدار v0.7.0.

يشبه هذا الحدثُ الحدثَ cluster.on('exit')‎ باستثناء أنَّه يمكن استعماله بشكل خاص مع الصنف Worker.

cluster.fork().on('listening', (address) => {
  // العمليَّة العاملة تستمع الآن
});

لا يُطلق هذا الحدث ضمن العمليَّة العاملة.

الحدث 'message'

أضيف في الإصدار v0.7.0.

هذا الحدث مشابهٌ للحدث 'message' الخاص بوحدة العناقيد (تجده في الأسفل) باستثناء أنَّه يمكن استعماله بشكل خاص مع الصنف Worker.

يمكن استدعاء process.on('message')‎ أحيانًا ضمن العمليَّة العاملة.

انظر أيضًا الحدث 'message' في الكائن process.

في المثال التالي، سننشئ عنقودًا يَعُدُ الطلبيات في العمليَّة الرئيسية باستعمال نظام الرسائل:

const cluster = require('cluster');
const http = require('http');

if (cluster.isMaster) {

  // http الاستمرار في تعقب طلبيات
  let numReqs = 0;
  setInterval(() => {
    console.log(`numReqs = ${numReqs}`);
  }, 1000);

  // عدُّ الطلبيات
  function messageHandler(msg) {
    if (msg.cmd && msg.cmd === 'notifyRequest') {
      numReqs += 1;
    }
  }

  // notifyRequest بدء العمليات العاملة والاستماع إلى الرسائل التي تحوي العبارة
  const numCPUs = require('os').cpus().length;
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  for (const id in cluster.workers) {
    cluster.workers[id].on('message', messageHandler);
  }

} else {

  // http تملك العمليات العاملة خادم
  http.Server((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');

    // تنبيه العمليَّة الرئيسية حول الطلبية
    process.send({ cmd: 'notifyRequest' });
  }).listen(8000);
}

الحدث 'online'

أضيف في الإصدار v0.7.0.

يشبه هذا الحدثُ الحدثَ cluster.on('online')‎ باستثناء أنَّه يمكن استعماله بشكل خاص مع الصنف Worker.

cluster.fork().on('online', () => {
  // العمليَّة العاملة متصلة الآن
});

لا يُطلق هذا الحدث ضمن العمليَّة العاملة.

worker.disconnect()‎

سجل التغييرات
الإصدار التغييرات
v7.3.0 أصبح هذا التابع يعيد مرجعًا إلى الكائن worker.
v0.0.7 أضيف هذا التابع.
  • القيم المعادة: <cluster.Worker> مرجعٌ إلى الكائن worker.

إن استدعي هذا التابع ضمن عمليَّة عاملة، فستُغلَق جميع الخوادم، وستنتظر هذه الخوادم الحدث 'close'، ثمَّ ستُفصَل قناة الاتصال IPC.

أمَّا إن استدعي هذا التابع ضمن عمليَّة رئيسية، فسترسل رسالة داخلية إلى العمليَّة العاملة تطلب منها استدعاء ‎.disconnect()‎ من تلقاء نفسها.

يتسبَّب هذا التابع أيضًا في ضبط الخاصية ‎.exitedAfterDisconnect.

انتبه إلى أنَّه لن تُقبَل الاتصالات الجديدة بعد إغلاق الخادم، وقد تقبل هذه الاتصالات عبر أيَّة عمليَّة عاملة مستمعة أخرى. أضف إلى ذلك أنَّ إغلاق جميع الاتصالات يسمح بإغلاق الخادم بشكل طبيعي. عندما لم يعد هنالك أيَّة اتصالات موجودة، ستُغلَق القناة IPC للعمليَّة العاملة مما يسمح بموت العمليَّة بأمان (ارجع إلى التابع server.close()‎ للمزيد من التفاصيل).

ينطبق كل ما سبق على اتصالات الخادم فقط، إذ لا تُغلِق العمليات العاملة اتصالات العميل تلقائيًّا ولا تنتظر عمليَّة قطع الاتصال إغلاق تلك الاتصالات قبل الخروج.

انتبه إلى وجود التابع process.disconnect()‎ في العمليَّة العاملة، إذ ليس هذا التابع هو التابع الذي نتحدث عنه بل هو التابع subprocess.disconnect()‎.

لمَّا كانت اتصالات الخادم التي تدوم لفترة طويلة تمنع أحيانًا العمليات الأبناء من قطع الاتصال، فهذا قد يكون مفيدًا لإرسال رسالة تنبِّه التطبيق لاتخاذ إجراءٍ محدَّدٍ تُغلَق عبره تلك الاتصالات. يمكن أيضًا أن يكون ذلك مفيدًا لتطبيق مهلة زمنية محدَّدة، إذ يمكن قتل العمليَّة العاملة إن لم يُطلَق الحدث 'disconnect' بعد فترة زمنيَّة معيَّنة.

if (cluster.isMaster) {
  const worker = cluster.fork();
  let timeout;

  worker.on('listening', (address) => {
    worker.send('shutdown');
    worker.disconnect();
    timeout = setTimeout(() => {
      worker.kill();
    }, 2000);
  });

  worker.on('disconnect', () => {
    clearTimeout(timeout);
  });

} else if (cluster.isWorker) {
  const net = require('net');
  const server = net.createServer((socket) => {
    // لن تنتهي الاتصالات مطلقًا
  });

  server.listen(8000);

  process.on('message', (msg) => {
    if (msg === 'shutdown') {
      // بدء إغلاق أية اتصالات للخادم
    }
  });
}

الخاصية worker.exitedAfterDisconnect

أضيفت في الإصدار v6.0.0.

تُضبَط هذه الخاصية عند استدعاء التابع ‎.kill()‎ أو التابع ‎.disconnect()‎، وتكون قيمتها قبل ذلك undefined.

تساعد هذه الخاصية على التمييز بين الخروج الطوعي والخروج العَرَضي، إذ قد تتخذ العمليَّة الرئيسية قرارًا يتعلق بإعادة توليد العمليَّة أم لا بناءً على قيمة هذه الخاصية.

cluster.on('exit', (worker, code, signal) => {
  if (worker.exitedAfterDisconnect === true) {
    console.log('Oh, it was just voluntary – no need to worry');
  }
});

// قتل العمليَّة العاملة
worker.kill();

الخاصية worker.id

أضيفت في الإصدار v0.8.0.

تمثِّل هذه الخاصية المعرِّف الفريد المعطى للعمليَّة العاملة. طالما كانت العمليَّة على قيد الحياة، تكون قيمة هذه الخاصية هي المفتاح الذي يشير إلى تلك العمليَّة في cluster.workers.

worker.isConnected()‎

أضيف في الإصدار v0.11.14.

يعيد هذا التابع القيمة true إن كانت العمليَّة العاملة متصلةً مع العمليَّة الرئيسية لها عبر قناة الاتصال IPC، أو تعيد القيمة false خلاف ذلك. تتصل العمليَّة العاملة مع العمليَّة الرئيسية لها بعد إنشائها، ويُقطع الاتصال بينهما بعد إطلاق الحدث 'disconnect'.

worker.isDead()‎

أضيف في الإصدار v0.11.14.

يعيد هذا التابع القيمة true إن أُنهيَت العمليَّة العاملة (سواءً خرجت العمليَّة بطوعها أو أُرسلت إشارة تجبرها على التوقف). خلا ذلك، تعيد القيمة false.

worker.kill([signal='SIGTERM'])‎

أضيف في الإصدار v0.9.12.

  • signal: ‏<string> اسم الإشارة المراد إرسالها إلى العمليَّة العاملة. القيمة الافتراضية هي: 'SIGTERM'.

يُستعمَل هذ التابع لقتل العمليَّة العاملة عند استدعائها هنالك. إن استدعي في العمليَّة الرئيسية، فسيُفعِّل ذلك عبر قطع اتصال worker.process ثمَّ -متى ما قُطِع الاتصال- يرسل الإشارة signal إلى العمليَّة العاملة لقتلها. أمَّا إن استدعي في العمليَّة العاملة نفسها، فسيقطع اتصال القناة أولًا ثمَّ يجبر العمليَّة على الخروج مع حال الخروج 0.

يتسبَّب هذا التابع أيضًا في ضبط الخاصية ‎.exitedAfterDisconnect.

يُعدُّ هذا التابع اسمًا بديلًا للتابع worker.destroy()‎، إذ أُوجِد من أجل التوافقية الرجوعيَّة (backwards compatibility)

ملاحظة: في العمليَّة العاملة، يكون التابع process.kill()‎ هو المسؤول عن قتل العمليَّة وليس هذا التابع.

الخاصية worker.process

أضيفت في الإصدار v0.7.0.

  • <ChildProcess>

تنشأ جميع العمليات العاملة باستعمال التابع child_process.fork()‎ ويُخزَّن الكائن المعاد بعد استدعائه في الخاصية ‎.process، وسيصبح المتغير العام process متاحًا في العملية العاملة.

انظر أيضًا توثيق العمليات الأبناء للمزيد من التفاصيل.

انتبه أيضًا إلى أنَّ العمليات العاملة ستستدعي process.exit(0)‎ إن أطلق الحدث 'disconnect' مع الكائن process وقيمة الخاصية ‎.exitedAfterDisconnect آنذاك هي false. هذا يساعد على تجنب الخروج عند انقطاع الاتصال بشكل غير مقصود.

worker.send(message[, sendHandle][, callback])‎

سجل التغييرات
الإصدار التغييرات
v4.0.0 أضيف المعامل callback.
v0.7.0 أضيف هذا التابع.

يرسل هذا التابع رسالةً إلى العمليَّة العاملة أو العمليَّة الرئيسية مع إمكانية إرسالة معالج أيضًا.

إن استدعي هذا التابع في عمليَّة رئيسية، فسيُرسل رسالةً إلى عمليَّة عاملة محدَّدة، ويكافي في هذه الحالة التابع ChildProcess.send()‎.

أمَّا إن استدعي في العمليَّة العاملة، فسيرسل رسالةً إلى العمليَّة الرئيسية لها، ويكافئ في هذه الحالة التابع process.send()‎.

يوضِّح المثال التالي كيفية إرجاع الرسائل المرسلة من العمليَّة الرئيسية إليها:

if (cluster.isMaster) {
  const worker = cluster.fork();
  worker.send('hi there');

} else if (cluster.isWorker) {
  process.on('message', (msg) => {
    process.send(msg);
  });
}

الحدث 'exit'

أضيف في الإصدار v0.7.9.

  • worker: ‏<cluster.Worker>
  • code: ‏<number> حالة الخروج إن تم الخروج بشكل طبيعي.
  • signal: ‏<string> اسم الإشارة التي تسببت في قتل العمليَّة، مثل 'SIGHUP'.

يُطلَق الحدث 'exit' عندما تخرج أو تتوقف أي عمليَّة عاملة.

يمكن أن يُستعمَل هذا الحدث لإعادة تشغيل العمليَّة من جديد عبر استدعاء ‎.fork()‎ مجدَّدًا.

cluster.on('exit', (worker, code, signal) => {
  console.log('worker %d died (%s). restarting...',
              worker.process.pid, signal || code);
  cluster.fork();
});

انظر أيضًا قسم «الحدث 'exit'» في توثيق العمليات الأبناء.

الحدث 'fork'

أضيف في الإصدار v0.7.0.

  • worker: ‏<cluster.Worker>

يُطلَق الحدث 'fork' عندما تتفرَّع عمليَّة عاملة جديدة. يمكن الاستفادة من هذا الحدث في مراقبة نشاط عميلة عاملة معيَّنة أو تحديد مهلة زمنيَّة مخصصة لها.

const timeouts = [];
function errorMsg() {
  console.error('Something must be wrong with the connection ...');
}

cluster.on('fork', (worker) => {
  timeouts[worker.id] = setTimeout(errorMsg, 2000);
});
cluster.on('listening', (worker, address) => {
  clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', (worker, code, signal) => {
  clearTimeout(timeouts[worker.id]);
  errorMsg();
});

الحدث 'listening'

أضيف في الإصدار v0.7.0.

  • worker:‏ <cluster.Worker>
  • address:‏ <Object>

يُطلَق الحدث 'listening' في الخادم بعد استدعاء listen()‎ من عمليَّة عاملة، ويُطلَق أيضًا في cluster في العمليَّة الرئيسية.

يُنفَّذ معالج هذا الحدث مع وسيطين؛ الأول هو worker الذي يحتوي على الكائن worker، والثاني هو address الذي يحتوي على خاصِّيَّات الاتصال address، و port، و addressType. هذا مفيد جدًا عندما تستمع العمليَّة العاملة إلى أكثر من عنوان واحد.

cluster.on('listening', (worker, address) => {
  console.log(
    `A worker is now connected to ${address.address}:${address.port}`);
});

يأخذ المعامل addressType إحدى القيم التالية:

  • 4: تشير إلى TCPv4، أو
  • 6: تشير إلى TCPv6، أو
  • -1: تشير إلى مقبس نطاق يونكس (unix domain socket)، أو
  • 'udp4' أو 'udp6': تشير إلى UDPv4 أو UDPv6 على التوالي.

الحدث 'message'

سجل التغييرات
الإصدار التغييرات
v6.0.0 أصبح بالإمكان تمرير المعامل worker الآن.
v2.5.0 أضيف هذا الحدث.

يُطلَق هذا الحدث عندما يستلم العنقود الرئيسي رسالةً من أي عمليَّة عاملة.

انظر أيضًا الحدث 'message' في توثيق العمليات الأبناء.

ملاحظة: قبل الإصدار Node.js v6.0، كان هذا الحدث يُطلِق المعامل message والمعامل handle فقط دون المعامل worker أي عكس ما كان مذكورًا في التوثيق.

يُطلَب استعمال الإصدارات الأحدث دومًا إن كانت مدعومة. مع ذلك، يمكن الاستغناء عن الكائن worker، إذ من الممكن التغلب على هذا التناقض عبر التحقُّق من عدد المعاملات بالشكل التالي:

cluster.on('message', (worker, message, handle) => {
  if (arguments.length === 2) {
    handle = message;
    message = worker;
    worker = undefined;
  }
  // ...
});

الحدث 'online'

أضيف في الإصدار v0.7.0.

  • worker:‏ <cluster.Worker>

يجب على العمليَّة العاملة الجديدة أن ترسل رسالة إلى العمليَّة الرئيسية تخبره بأنَّها أصبحت متصلة. عندما تستلم العمليَّة الرئيسية هذه الرسالة، يُطلَق هذا الحدث. الفارق بين الحدث 'fork' والحدث 'online' هو أنَّ الأول يُطلَق عندما تنشئ العمليَّة الرئيسية عمليَّة عاملة، والثاني يطلق عندما تبدأ العمليَّة العاملة في العمل.

cluster.on('online', (worker) => {
 console.log('Yay, the worker responded after it was forked');
});

الحدث 'setup'

أضيف في الإصدار v0.7.1.

يُطلَق هذا الحدث في كل مرة يُستدعَى فيها التابع ‎.setupMaster()‎.

يصبح الكائن settings هو الكائن cluster.settings نفسه في الوقت الذي يستدعى فيه التابع ‎.setupMaster()‎ وهو توجيهي (advisory) فقط، إذ من الممكن استدعاء ‎.setupMaster()‎ عدَّة مرات بضغطة بسيطة.

إن كانت الدقة مهمة جدًا، فاستعمل الكائن cluster.settings.

cluster.disconnect([callback])‎

أضيف في الإصدار v0.7.7.

  • callback: ‏<Function> دالةٌ تستدعى عندما تصبح جميع العمليات العاملة غير متصلة وتغلق جميع المعالجات.

يستدعى التابع ‎.disconnect()‎ مع كل عمليَّة عاملة في cluster.workers. عندما تصبح تلك العمليات غير متصلة وتغلق جميع معالجات الأحداث، سيؤدي ذلك إلى موت العمليَّة الرئيسية بأمان إن لم يكن هنالك أيَّة أحداث منتظرة.

يمكن تمرير دالة رد نداء إلى هذا التابع اختياريًّا لاستدعائها عند الانتهاء.

لا يمكن استدعاء هذا التابع إلا مع عمليَّة رئيسية.

cluster.fork([env])‎

أضيف في الإصدار v0.6.0.

  • env: ‏<Object> زوجٌ من مفتاح/قيمة يراد إضافته إلى بيئة العمليَّة العاملة.
  • القيم المعادة: <cluster.Worker>

يولِّد هذا التابع عمليَّة عاملة جديدة.

لا يمكن استدعاء هذا التابع إلا من العمليَّة الرئيسية.

الخاصية cluster.isMaster

أضيف في الإصدار v0.8.1.

تحدِّد هذه الخاصية إن كانت العمليَّة عمليَّةً رئيسيةً باستعمال process.env.NODE_UNIQUE_ID، إذ تكون قيمة هذه الخاصية true إن كانت قيمة process.env.NODE_UNIQUE_ID هي undefined.

الخلاصة، تكون قيمة هذه الخاصية true إن كانت العمليَّة عمليَّةً رئيسية.

الخاصية cluster.isWorker

أضيفت في الإصدار v0.6.0.

تحدِّد هذه الخاصية إن كانت العمليَّة عمليَّةً عاملة، إذ تكون قيمتها true إن لم تكن العمليَّة عمليَّةً رئيسية (هي عكس الخاصية cluster.isMaster تمامًا).

الخاصية cluster.schedulingPolicy

أضيفت في الإصدار v0.11.2.

تمثِّل هذه الخاصية أسلوب الجدولة، إذ إمَّا أن تكون قيمتها cluster.SCHED_RR من أجل أسلوب التناوب (round-robin) أو cluster.SCHED_NONE لترك الأمر إلى نظام التشغيل. هذه الخاصية هي خاصية عامة وتكون غير فعالة ولا يمكن ضبطها متى ما ولدت أول عمليَّة عاملة أو متى ما استدعي التابع cluster.setupMaster()‎ أيهما يحدث أولًا.

القيمة الافتراضيَّة لهذه الخاصية هي SCHED_RR على جميع المنصات باستثناء ويندوز. سيغيِّر ويندوز قيمة هذه الخاصية إلى SCHED_RR متى ما توافرت المكتبة libuv لنشر المعالجات IOCP بكفاءة دون استهلاك قدر كبير من الأداء.

يمكن ضبط الخاصية cluster.schedulingPolicy عبر متغير البيئة NODE_CLUSTER_SCHED_POLICY، إذ يمكن استعمال إحدى القيمتين: 'rr' أو 'none'.

الخاصية cluster.settings

سجل التغييرات
الإصدار التغييرات
v9.5.0 إضافة الخيار cwd.
v9.4.0 إضافة الخيار windowsHide.
v8.2.0 إضافة الخيار inspectPort.
v6.4.0 إضافة الخيار stdio.
v0.7.1 إضافة هذه الخاصية.
  • options: ‏<Object> كائنٌ يمكن أن يحتوي على الخيارات التالية:
    • execArgv: ‏<string[]‎‎> قائمة سلاسل نصية تمثِّل الوسائط المراد تمريرها إلى عمليَّة Node.js القابلة للتنفيذ. القيمة الافتراضيَّة هي: process.execArgv.
    • exec: ‏<string> المسار الذي يشير إلى ملف العمليَّة العاملة. القيمة الافتراضية هي: process.argv[1]‎.
    • args: ‏<string[]‎‎> قائمةٌ من سلاسل نصية تمثِّل الوسائط المراد تمريرها إلى العمليَّة العاملة. القيمة الافتراضية هي: process.argv.slice(2)‎.
    • cwd: ‏<string> سلسلةٌ نصيةٌ تمثِّل مجلد العمل الحالي للعمليَّة العاملة. القيمة الافتراضيَّة هي: undefined (تورث من العمليَّة الأب).
    • silent: ‏<boolean> قيمة منطقيَّة تحدِّد إن كان يراد إسال المخرجات إلى مجرى الخرج القياسي للعمليَّة الأب أم لا. القيمة الافتراضية هي: false.
    • stdio: ‏<Array> ضبط مجاري الخرج والدخل القياسية للعمليَّة الفرعية. لمَّا كانت الوحدة cluster تعتمد على القناة IPC، فيجب أن يحوي هذا الضبط على 'ipc'. عندما يعطى هذا الضبط، يُتجاهل الضبط silent.
    • uid: ‏<number> عددٌ يحدِّد مُعرِّف المستخدم المالك للعمليَّة (ارجع إلى setuid(2)‎).
    • gid: ‏<number> عددٌ يحدِّد مُعرِّف المجموعة المالكة للعمليَّة (ارجع إلى setgid(2)‎).
    • inspectPort: ‏<number> | ‏<Function> يعين منفذ مراقب للعمليَّة العاملة. يمكن أن يكون هذا الضبط عددًا أو دالةً تعيد عددًا دون أن يُمرَّر إليها أي وسيط. افتراضيًّا، يكون لكل عمليَّة عاملة منفذًا خاصًّا بها يتزايد بدءًا من المنفذ process.debugPort الخاص بالعمليَّة الرئيسية.
    • windowsHide: ‏<boolean> قيمةٌ منطقيةٌ تتحكم بإظهار وإخفاء نافذة الطرفية للعمليَّة الفرعية التي ستُنشأ دومًا بشكل اعتيادي في أنظمة ويندوز. القيمة الافتراضيَّة هي: false.

سيحتوي الكائن settings على الإعدادات بعد استدعاء التابع ‎.setupMaster()‎ (أو التابع ‎.fork()) من ضمنها الإعدادات الافتراضية.

لا يفترض تغيير هذا الكائن أو ضبطه يدويًّا.

cluster.setupMaster([settings])‎

سجل التغييرات
الإصدار التغييرات
v6.4.0 أضيف الخيار stdio.
v0.7.1 أضيف هذا التابع.
  • settings: ‏<Object> ارجع إلى الخاصية cluster.settings.

يستعمل هذا التابع لتغيير سلوك 'fork' الافتراضي، إذ ستصبح الإعدادات المضبوطة في الخاصية cluster.settings هي الإعدادات الحاليَّة متى ما استدعي.

يرجى الانتباه إلى النقاط التالية:

  • ستصبح التغييرات الحاصلة سارية المفعول مع الاستدعاءات اللاحقة للتابع ‎.fork()‎ فقط ولن تؤثر مطلقًا على العمليات العاملة التي تعمل مسبقًا.
  • الخاصية الوحيدة التي لا يمكن للعمليَّة العاملة عبر ‎.setupMaster()‎ هي env المُمرَّرة إلى التابع ‎.fork()‎.
  • تطبق الإعدادات الافتراضيَّة المذكورة في الأعلى عند استدعاء التابع ‎.setupMaster()‎ لأول مرة فقط، ثمَّ تصبح الاعدادات الافتراضية للاستدعاءات اللاحقة هي القيم الحالية المضبوطة في الوقت الذي يستدعى التابع ‎.setupMaster()‎ فيه.

تفحَّص المثال التالي:

const cluster = require('cluster');
cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'https'],
  silent: true
});
cluster.fork(); // عاملة http عمليَّة
cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'http']
});
cluster.fork(); // عاملة http عمليَّة

لا يمكن تنفيذ هذه الشيفرة إلا من عمليَّة رئيسية.

الخاصية cluster.worker

أضيفت في الإصدار v0.7.0.

تمثل هذه الخاصية مرجعًا إلى الكائن worker الحالي، وهي غير متاحة في العمليَّة الرئيسية.

const cluster = require('cluster');

if (cluster.isMaster) {
  console.log('I am master');
  cluster.fork();
  cluster.fork();
} else if (cluster.isWorker) {
  console.log(`I am worker #${cluster.worker.id}`);
}

الخاصية cluster.workers

أضيفت في الإصدار v0.7.0.

تُخزِّن هذه الخاصية الكائنات worker الفعالة آنذاك، إذ يربط كل كائن مع معرِّف id. يمكن الاستفادة من هذه الخاصية بتطبيق حلقة تكراريَّة على جميع العمليات العاملة، وهي متاحةٌ فقط في العمليَّة الرئيسية.

تزال العمليَّة العاملة من cluster.workers بعد أن تصبح غير متصلة ثمَّ تخرج، ولا يمكن أن يحدَّد ترتيب وقوع هذين الحدثين (الحدث 'disconnected' والحدث 'exit') مسبقًا. مع ذلك، يُضمن بإزالة العمليَّة العاملة من القائمة cluster.workers قبل إطلاق آخر الحدثين 'disconnected' و 'exit'.

// تطبيق حلقة تكرارية لفعل شيء مع جميع العمليات العاملة
function eachWorker(callback) {
  for (const id in cluster.workers) {
    callback(cluster.workers[id]);
  }
}
eachWorker((worker) => {
  worker.send('big announcement to all workers');
});

استعمال مُعرِّف العمليَّة العاملة الفريد هو أسهل طريقة لتحديد هذه العمليَّة.

socket.on('data', (id) => {
  const worker = cluster.workers[id];
});

مصادر