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

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

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

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

سنعمل على إنشاء خادم ويب بسيط باستخدام Koa ، والذي يشبه إلى حد بعيد Express من حيث الاستخدام.

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

ما سنبنيه

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

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

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

إعداد المشروع

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

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

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

الشروع في العمل مع Koa

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

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

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

في دليل الوحدة النمطية ، سنعمل على إنشاء ملفين: job.js و log.js.

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

وحدة السجل

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

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

وحدة الوظيفة

سأكون صادقًا ، هذا ليس نصًا جميلًا وفائق التحسين. إنها مجرد مهمة سهلة تتيح لنا التأكيد على أجهزتنا.

خادم الويب Koa

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

سنكون قادرين على تنفيذ طلب GET على http: // localhost: 3000 /. سيرد Koa بنص بسيط يوضح لنا رقم التعريف الشخصي الحالي (معرف العملية).

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

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

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

المشكلة

الصورة التي أتعامل معها حاليًا (عبر Unsplash)

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

  • ماك بوك برو 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٪!

تقديم المجموعات

جميع الاعتمادات إلى Pexels

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

مضيفا Koa

كل الائتمان ل Pexels

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

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

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

أول واحد سوف يخبرنا أنه تم إنتاج عامل جديد. العامل الثاني سينشئ عاملًا جديدًا عند تعطل عامل آخر.

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

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

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

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

خاتمة

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

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

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

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

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