كيفية توسيع نطاق خادم Node.js الخاص بك باستخدام نظام المجموعات

تعد قابلية التوسع موضوعًا ساخنًا في مجال التكنولوجيا ، وتوفر كل لغة أو إطار عمل برمجة طريقتها الخاصة في التعامل مع كميات كبيرة من حركة المرور.

اليوم ، سنرى مثالًا سهلًا ومباشرًا حول تجميع Node.js. هذه تقنية برمجة ستساعدك على موازنة شفرتك وتسريع الأداء.

"يتم تشغيل مثيل واحد من Node.js في سلسلة محادثات واحدة. للاستفادة من الأنظمة متعددة المراكز ، سيرغب المستخدم في بعض الأحيان في إطلاق مجموعة من عمليات Node.js للتعامل مع الحمل ".

- توثيق Node.js

سننشئ خادم ويب بسيطًا باستخدام Koa ، والذي يشبه حقًا Express من حيث الاستخدام.

المثال الكامل متاح في مستودع Github هذا.

ما سنبنيه

سنقوم ببناء خادم ويب بسيط يعمل على النحو التالي:

  1. سيتلقى خادمنا POSTطلبًا ، وسنتظاهر بأن المستخدم يرسل لنا صورة.
  2. سنقوم بنسخ صورة من نظام الملفات إلى دليل مؤقت.
  3. سنقلبها رأسيًا باستخدام Jimp ، مكتبة معالجة الصور لـ Node.js.
  4. سنقوم بحفظه في نظام الملفات.
  5. سنحذفه وسنرسل ردًا إلى المستخدم.

بالطبع ، هذا ليس تطبيقًا حقيقيًا ، ولكنه قريب جدًا من أحد التطبيقات. نريد فقط قياس فوائد استخدام التجميع.

اقامة المشروع

سأستخدم yarnلتثبيت التبعيات الخاصة بي وتهيئة مشروعي:

نظرًا لأن Node.js مترابط واحدًا ، إذا تعطل خادم الويب الخاص بنا ، فسيظل معطلاً حتى يتم إعادة تشغيله من خلال عملية أخرى. لذلك سنقوم بتثبيت برنامج خفي بسيط إلى الأبد والذي سيعيد تشغيل خادم الويب الخاص بنا إذا تعطل.

سنقوم أيضًا بتثبيت Jimp و Koa و Koa Router.

الابتداء مع Koa

هذا هو هيكل المجلد الذي نحتاج إلى إنشائه:

سيكون لدينا srcمجلد يحتوي على ملفين جافا سكريبت: cluster.jsو standard.js.

سيكون الملف الأول هو الملف حيث سنقوم بتجربة clusterالوحدة. والثاني هو خادم Koa البسيط الذي سيعمل بدون أي تجميع.

في moduleالدليل ، سننشئ ملفين: job.jsو log.js.

job.jsسيتم تنفيذ أعمال معالجة الصور. log.jsسيسجل كل حدث يحدث أثناء تلك العملية.

وحدة السجل

ستكون وحدة السجل عبارة عن وظيفة بسيطة تأخذ وسيطة وستكتبها إلى stdout(على غرار console.log).

سيقوم أيضًا بإلحاق الطابع الزمني الحالي في بداية السجل. سيتيح لنا ذلك التحقق من وقت بدء العملية وقياس أدائها.

وحدة الوظيفة

سأكون صريحًا ، هذا ليس نصًا جميلًا ومحسّنًا للغاية. إنها مجرد مهمة سهلة تتيح لنا الضغط على أجهزتنا.

خادم الويب Koa

سنقوم بإنشاء خادم ويب بسيط للغاية. سوف يستجيب على مسارين بطريقتين مختلفتين HTTP.

سنكون قادرين على تنفيذ طلب GET في //localhost:3000/. سوف يستجيب Koa بنص بسيط سيظهر لنا PID الحالي (معرف العملية).

سيقبل المسار الثاني طلبات POST فقط على /flipالمسار ، وسيقوم بالمهمة التي أنشأناها للتو.

سننشئ أيضًا برمجية وسيطة بسيطة ستحدد X-Response-Timeرأس الصفحة. سيسمح لنا هذا بقياس الأداء.

عظيم! يمكننا الآن بدء كتابة الخادم node ./src/standard.jsواختبار طرقنا.

المشكلة

لنستخدم جهازي كخادم:

  • Macbook Pro 15 بوصة 2016
  • 2.7 جيجا هرتز انتل كور i7
  • 16 جيجا رام

إذا قدمت طلب POST ، فسيرسل البرنامج النصي أعلاه لي ردًا في حوالي 3800 مللي ثانية. ليس سيئًا للغاية ، نظرًا لأن الصورة التي أعمل عليها حاليًا تبلغ حوالي 6.7 ميغابايت.

يمكنني محاولة تقديم المزيد من الطلبات ، لكن وقت الاستجابة لن ينخفض ​​كثيرًا. هذا لأنه سيتم تنفيذ الطلبات بالتتابع.

إذن ، ماذا سيحدث إذا حاولت تقديم 10 ، 100 ، 1000 طلب متزامن؟

لقد أنشأت برنامج نصي Elixir بسيطًا ينفذ طلبات HTTP متزامنة متعددة:

اخترت Elixir لأنه من السهل حقًا إنشاء عمليات متوازية ، ولكن يمكنك استخدام ما تفضله!

اختبار عشرة طلبات متزامنة - بدون تجميع

كما ترون ، ننتج 10 عمليات متزامنة من iex (Elixir REPL).

سيقوم خادم Node.js بنسخ صورتنا على الفور والبدء في قلبها.

سيتم تسجيل الاستجابة الأولى بعد 16 ثانية والأخيرة بعد 40 ثانية.

يا له من انخفاض كبير في الأداء! مع 10 طلبات متزامنة فقط ،خفضنا أداء خادم الويب بنسبة 950٪!

إدخال التجميع

تذكر ما ذكرته في بداية المقال؟

للاستفادة من الأنظمة متعددة النواة ، سيرغب المستخدم أحيانًا في تشغيل مجموعة من عمليات Node.js للتعامل مع الحمل.

اعتمادًا على الخادم الذي سنقوم بتشغيل تطبيق Koa الخاص بنا ، يمكن أن يكون لدينا عدد مختلف من النوى.

كل نواة ستكون مسؤولة عن التعامل مع الحمل بشكل فردي. بشكل أساسي ، سيتم تلبية كل طلب HTTP من خلال نواة واحدة.

على سبيل المثال - جهازي ، الذي يحتوي على ثمانية مراكز ، سيتعامل مع ثمانية طلبات متزامنة.

يمكننا الآن حساب عدد وحدات المعالجة المركزية (CPU) لدينا بفضل osالوحدة:

و cpus()سوف الأسلوب بإرجاع مجموعة من الكائنات التي تصف وحدات المعالجة المركزية لدينا. يمكننا ربط طوله بثابت يسمى numWorkers، "لأن هذا هو عدد العمال الذين سنستخدمهم.

نحن الآن جاهزون لطلب clusterالوحدة.

نحن الآن بحاجة إلى طريقة لتقسيم عمليتنا الرئيسية إلى Nعمليات متميزة.

سوف نسمي عمليتنا الرئيسية masterوالعمليات الأخرى workers.

clusterتقدم الوحدة النمطية Node.js طريقة تسمى isMaster. سيعيد قيمة منطقية تخبرنا ما إذا كانت العملية الحالية موجهة بواسطة عامل أو خبير:

عظيم. القاعدة الذهبية هنا هي أننا لا نريد خدمة تطبيق Koa الخاص بنا في إطار العملية الرئيسية.

نريد إنشاء تطبيق Koa لكل عامل ، لذلك عندما يأتي طلب ما ، سيتولى العامل المجاني الأول العناية به.

و cluster.fork()سوف طريقة تناسب هدفنا:

حسنًا ، في البداية قد يكون هذا صعبًا بعض الشيء.

كما ترى في النص أعلاه ، إذا تم تنفيذ البرنامج النصي بواسطة العملية الرئيسية ، فسنعلن عن ثابت يسمى workers. سيؤدي هذا إلى إنشاء عامل لكل نواة من وحدة المعالجة المركزية الخاصة بنا ، وسوف يخزن جميع المعلومات المتعلقة بهم.

إذا كنت غير متأكد من الصيغة المعتمدة ، فإن الاستخدام […Array(x)].map()هو نفسه تمامًا مثل:

أنا فقط أفضل استخدام القيم غير القابلة للتغيير أثناء تطوير تطبيق عالي التزامن.

مضيفا Koa

كما قلنا من قبل ، لا نريد تقديم تطبيق Koa الخاص بنا في إطار العملية الرئيسية.

دعنا ننسخ بنية تطبيق Koa في elseالعبارة ، لذلك سنتأكد من أن العامل سيقدمها:

كما ترى ، أضفنا أيضًا اثنين من مستمعي الحدث في isMasterالبيان:

سيخبرنا الأول أن عاملاً جديدًا قد ولد. العامل الثاني سيخلق عاملًا جديدًا عندما يصطدم عامل آخر.

بهذه الطريقة ، ستكون العملية الرئيسية مسؤولة فقط عن إنشاء عمال جدد وتنظيمهم. سيخدم كل عامل مثيلًا من Koa يمكن الوصول إليه على :3000المنفذ.

اختبار عشرة طلبات متزامنة - مع التجميع

كما ترى ، تلقينا ردنا الأول بعد حوالي 10 ثوانٍ ، والأخير بعد حوالي 14 ثانية. إنه تحسن مذهل مقارنة بوقت الاستجابة البالغ 40 ثانية!

لقد قدمنا ​​عشرة طلبات متزامنة ، وأخذ خادم Koa ثمانية طلبات على الفور. عندما أرسل العامل الأول رده إلى العميل ، أخذ أحد الطلبات المتبقية ومعالجته!

استنتاج

يتمتع Node.js بقدرة مذهلة على التعامل مع الأحمال الكبيرة ، ولكن لن يكون من الحكمة إيقاف الطلب حتى ينتهي الخادم من عمليته.

في الواقع ، يمكن لخوادم الويب Node.js التعامل مع آلاف الطلبات المتزامنة فقط إذا أرسلت ردًا على الفور إلى العميل.

تتمثل أفضل الممارسات في إضافة واجهة بريد عام / فرعي باستخدام Redis أو أي أداة أخرى مذهلة. عندما يرسل العميل طلبًا ، يبدأ الخادم اتصالًا في الوقت الفعلي مع الخدمات الأخرى. هذا يتولى مسؤولية الوظائف باهظة الثمن.

تساعد موازنات التحميل أيضًا في تقسيم أحمال المرور العالية.

مرة أخرى ، تمنحنا التكنولوجيا إمكانيات لا نهاية لها ، ونحن على يقين من إيجاد الحل المناسب لتوسيع نطاق تطبيقنا إلى ما لا نهاية وما بعدها!