إنشاء خدمة حانة / فرعية داخل المنزل باستخدام Node.js و Redis
نشرت: 2022-03-10يعمل عالم اليوم في الوقت الفعلي. سواء كان الأمر يتعلق بتداول الأسهم أو طلب الطعام ، يتوقع المستهلكون اليوم نتائج فورية. وبالمثل ، نتوقع جميعًا معرفة الأشياء على الفور - سواء كانت في الأخبار أو الرياضة. بعبارة أخرى ، الصفر هو البطل الجديد.
ينطبق هذا أيضًا على مطوري البرامج - يمكن القول إن بعض الأشخاص الذين نفد صبرهم! قبل الغوص في قصة BrowserStack ، سأكون مقصراً مني عدم تقديم بعض المعلومات الأساسية حول Pub / Sub. لأولئك الذين هم على دراية بالأساسيات ، لا تتردد في تخطي الفقرتين التاليتين.
تعتمد العديد من التطبيقات اليوم على نقل البيانات في الوقت الفعلي. لنلقِ نظرة فاحصة على مثال: الشبكات الاجتماعية. تنشئ أمثال Facebook و Twitter موجزات ذات صلة ، وأنت (عبر التطبيق الخاص بهم) تستهلكها وتتجسس على أصدقائك. إنهم يحققون ذلك من خلال ميزة المراسلة ، حيث إذا قام المستخدم بإنشاء بيانات ، فسيتم نشرها للآخرين ليستهلكها في لمح البصر. أي تأخيرات كبيرة وسيشتكي المستخدمون ، سينخفض الاستخدام ، وإذا استمر ، سينتج. إن المخاطر كبيرة وكذلك توقعات المستخدمين. إذن كيف تدعم خدمات مثل WhatsApp و Facebook و TD Ameritrade و Wall Street Journal و GrubHub كميات كبيرة من عمليات نقل البيانات في الوقت الفعلي؟
كل منهم يستخدم بنية برمجية مماثلة على مستوى عالٍ يسمى نموذج "نشر - اشتراك" ، يشار إليه عادة باسم Pub / Sub.
"في هندسة البرمجيات ، يعد النشر - الاشتراك نمطًا للمراسلة حيث لا يقوم مرسلو الرسائل ، الذين يطلق عليهم الناشرون ، ببرمجة الرسائل ليتم إرسالها مباشرة إلى مستلمين معينين ، يطلق عليهم المشتركون ، ولكن بدلاً من ذلك يصنفون الرسائل المنشورة إلى فئات دون معرفة المشتركين ، إذا أي ، قد يكون هناك. وبالمثل ، يعبر المشتركون عن اهتمامهم بفئة واحدة أو أكثر ولا يتلقون سوى الرسائل التي تهمهم ، دون معرفة الناشرين الموجودين ، إن وجد ".
- ويكيبيديا
بالملل من التعريف؟ العودة إلى قصتنا.
في BrowserStack ، تدعم جميع منتجاتنا (بطريقة أو بأخرى) البرامج التي تحتوي على عنصر تبعية كبير في الوقت الفعلي - سواء أكانت سجلات الاختبارات الآلية ، أو لقطات شاشة المستعرض المخبوزة حديثًا ، أو البث عبر الهاتف المحمول بمعدل 15 إطارًا في الثانية.
في مثل هذه الحالات ، إذا سقطت رسالة واحدة ، فقد يفقد العميل المعلومات الحيوية لمنع الخطأ . لذلك ، احتجنا إلى القياس لمتطلبات حجم البيانات المتنوعة. على سبيل المثال ، مع خدمات مسجل الجهاز في وقت معين ، قد يكون هناك 50 ميغا بايت من البيانات التي تم إنشاؤها ضمن رسالة واحدة. مثل هذه الأحجام يمكن أن تعطل المتصفح. ناهيك عن أن نظام BrowserStack سيحتاج إلى توسيع نطاق المنتجات الإضافية في المستقبل.
نظرًا لاختلاف حجم البيانات لكل رسالة من بضع بايتات إلى 100 ميجابايت ، فقد احتجنا إلى حل قابل للتطوير يمكنه دعم العديد من السيناريوهات. بمعنى آخر ، سعينا إلى سيف يقطع كل الكعك. في هذه المقالة ، سأناقش سبب وكيفية ونتائج بناء خدمة Pub / Sub داخل الشركة.
من خلال عدسة مشكلة العالم الحقيقي الخاصة بـ BrowserStack ، ستحصل على فهم أعمق لمتطلبات وعملية إنشاء Pub / Sub الخاص بك .
حاجتنا إلى خدمة حانة / فرعي
يحتوي BrowserStack على حوالي 100 مليون + رسالة ، كل منها في مكان ما بين 2 بايت تقريبًا و 100+ ميجا بايت. يتم تمريرها حول العالم في أي لحظة ، وكل ذلك بسرعات إنترنت مختلفة.
أكبر مولدات هذه الرسائل ، حسب حجم الرسالة ، هي منتجاتنا BrowserStack Automate. يحتوي كلاهما على لوحات معلومات في الوقت الفعلي تعرض جميع الطلبات والاستجابات لكل أمر من أوامر اختبار المستخدم. لذلك ، إذا قام شخص ما بإجراء اختبار مع 100 طلب حيث يبلغ متوسط حجم استجابة الطلب 10 بايت ، فإن هذا يرسل 1 × 100 × 10 = 1000 بايت.
الآن دعونا ننظر إلى الصورة الأكبر - بالطبع - لا نجري اختبارًا واحدًا فقط في اليوم. يتم تشغيل ما يقرب من 850،000 من اختبارات BrowserStack Automate و App Automate باستخدام BrowserStack كل يوم. ونعم ، نحن في المتوسط حوالي 235 استجابة للطلب لكل اختبار. نظرًا لأنه يمكن للمستخدمين التقاط لقطات شاشة أو طلب مصادر الصفحة في السيلينيوم ، فإن متوسط حجم الاستجابة للطلب لدينا يقارب 220 بايت.
لذا ، بالعودة إلى الآلة الحاسبة الخاصة بنا:
850.000 × 235 × 220 = 43945.000.000 بايت (تقريبًا) أو 43.945 جيجا بايت فقط في اليوم
الآن دعنا نتحدث عن BrowserStack Live و App Live. بالتأكيد لدينا Automate كفائزنا في شكل حجم البيانات. ومع ذلك ، فإن المنتجات الحية تأخذ زمام المبادرة عندما يتعلق الأمر بعدد الرسائل التي تم تمريرها. لكل اختبار مباشر ، يتم تمرير حوالي 20 رسالة في كل دقيقة يتم تشغيلها. نجري حوالي 100000 اختبار مباشر ، والتي يبلغ متوسط كل اختبار حوالي 12 دقيقة مما يعني:
100،000 × 12 × 20 = 24،000،000 رسالة في اليوم
الآن بالنسبة للجزء الرائع والرائع: نقوم ببناء وتشغيل وصيانة التطبيق لهذا يسمى pusher مع 6 t1.micro مثيلات من ec2. تكلفة تشغيل الخدمة؟ حوالي 70 دولارًا في الشهر .
اختيار البناء مقابل الشراء
أول الأشياء أولاً: كشركة ناشئة ، مثل معظم الآخرين ، كنا دائمًا متحمسون لبناء الأشياء داخل الشركة. لكننا ما زلنا نقوم بتقييم بعض الخدمات هناك. المتطلبات الأساسية كانت:
- الموثوقية والاستقرار ،
- أداء عالي و
- الفعالية من حيث التكلفة.
دعنا نترك معايير الفعالية من حيث التكلفة ، حيث لا يمكنني التفكير في أي خدمات خارجية تقل تكلفتها عن 70 دولارًا في الشهر (أرسل لي رسالة على تويتر إذا كنت تعرف أنك تفعل ذلك!). لذا فإن إجابتنا هناك واضحة.
من حيث الموثوقية والاستقرار ، وجدنا الشركات التي قدمت Pub / Sub كخدمة مع وقت تشغيل بنسبة 99.9+ في المائة طبقًا لاتفاقية مستوى الخدمة ، ولكن كان هناك العديد من الشروط والأحكام المرفقة. المشكلة ليست بهذه البساطة كما تعتقد ، خاصة عندما تفكر في الأراضي الشاسعة للإنترنت المفتوح التي تقع بين النظام والعميل. يعرف أي شخص على دراية بالبنية التحتية للإنترنت أن الاتصال المستقر هو التحدي الأكبر. بالإضافة إلى ذلك ، يعتمد مقدار البيانات المرسلة على حركة المرور. على سبيل المثال ، قد ينفجر أنبوب البيانات الموجود عند الصفر لمدة دقيقة واحدة خلال اليوم التالي. نادرًا ما تكون الخدمات التي توفر موثوقية كافية أثناء لحظات الانفجار هذه (Google و Amazon).
يعني أداء مشروعنا الحصول على البيانات وإرسالها إلى جميع عقد الاستماع في وقت قريب من الصفر . في BrowserStack ، نستخدم الخدمات السحابية (AWS) جنبًا إلى جنب مع استضافة المواقع المشتركة. ومع ذلك ، يمكن وضع ناشرينا و / أو مشتركينا في أي مكان. على سبيل المثال ، قد يتضمن خادم تطبيق AWS يقوم بإنشاء بيانات السجل التي تشتد الحاجة إليها ، أو الأجهزة الطرفية (الأجهزة التي يمكن للمستخدمين الاتصال بها بشكل آمن للاختبار). بالعودة إلى قضية الإنترنت المفتوحة مرة أخرى ، إذا أردنا تقليل مخاطرنا ، فسيتعين علينا التأكد من أن Pub / Sub الخاص بنا يستفيد من أفضل الخدمات المضيفة و AWS.
من المتطلبات الأساسية الأخرى القدرة على نقل جميع أنواع البيانات (بايت ، نص ، بيانات وسائط غريبة ، إلخ). مع أخذ كل ذلك في الاعتبار ، لم يكن من المنطقي الاعتماد على حل جهة خارجية لدعم منتجاتنا. في المقابل ، قررنا إحياء روح بدء التشغيل لدينا ، ونشمر عن سواعدنا لبرمجة الحل الخاص بنا.
بناء الحل لدينا
يعني Pub / Sub حسب التصميم أنه سيكون هناك ناشر ، يقوم بإنشاء البيانات وإرسالها ، ويقبلها المشترك ويعالجها. هذا مشابه للراديو: قناة راديو تبث (تنشر) المحتوى في كل مكان داخل النطاق. بصفتك مشتركًا ، يمكنك تحديد ما إذا كنت تريد ضبط هذه القناة والاستماع (أو إيقاف تشغيل الراديو تمامًا).

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

أعلاه هو رسم تخطيطي يقدم مثالا على حانة / فرعي جيدة مع:
- الناشرون
هنا لدينا ناشرون يولدون رسائل بناءً على منطق محدد مسبقًا. في تشبيهنا بالراديو ، هؤلاء هم فرسان الراديو لدينا الذين يصنعون المحتوى. - المواضيع
يوجد نوعان هنا ، مما يعني وجود نوعين من البيانات. يمكننا القول أن هذه هي قنواتنا الإذاعية 1 و 2. - مشتركين
لدينا ثلاثة كل منها يقرأ البيانات حول موضوع معين. شيء واحد يجب ملاحظته هو أن المشترك 2 يقرأ من مواضيع متعددة. في تشبيهنا الإذاعي ، هؤلاء هم الأشخاص الذين تم ضبطهم على قناة راديو.
لنبدأ في فهم المتطلبات اللازمة للخدمة.
- مكون حدث
يبدأ هذا فقط عندما يكون هناك شيء ما لركله. - تخزين عابر
هذا يحافظ على استمرار البيانات لفترة قصيرة ، لذلك إذا كان المشترك بطيئًا ، فلا يزال لديه نافذة لاستهلاكها. - تقليل الكمون
ربط كيانين عبر شبكة مع الحد الأدنى من القفزات والمسافة.
اخترنا مجموعة تقنيات استوفت المتطلبات المذكورة أعلاه:
- Node.js
لماذا لا؟ في حالة وجوده ، لن نحتاج إلى معالجة بيانات مكثفة ، بالإضافة إلى أنه من السهل ضمه. - ريديس
يدعم البيانات قصيرة العمر تمامًا. لديه كل الإمكانيات للبدء والتحديث والانتهاء التلقائي. كما أنه يضع عبئًا أقل على التطبيق.
Node.js لاتصال منطق الأعمال
Node.js هي لغة مثالية تقريبًا عندما يتعلق الأمر بكتابة التعليمات البرمجية التي تتضمن IO والأحداث. كانت مشكلتنا المعينة لها كلا الأمرين ، مما يجعل هذا الخيار أكثر عملية لاحتياجاتنا.
بالتأكيد يمكن تحسين اللغات الأخرى مثل Java أو توفر لغة مثل Python قابلية التوسع. ومع ذلك ، فإن تكلفة البدء بهذه اللغات مرتفعة للغاية بحيث يمكن للمطور إنهاء كتابة التعليمات البرمجية في Node في نفس المدة.
لنكون صادقين ، إذا كانت الخدمة لديها فرصة لإضافة المزيد من الميزات المعقدة ، كان بإمكاننا النظر في لغات أخرى أو مكدس مكتمل. لكن هنا زواج مصنوع في الجنة. ها هي مجموعتنا. json :
{ "name": "Pusher", "version": "1.0.0", "dependencies": { "bstack-analytics": "*****", // Hidden for BrowserStack reasons. :) "ioredis": "^2.5.0", "socket.io": "^1.4.4" }, "devDependencies": {}, "scripts": { "start": "node server.js" } }
بكل بساطة ، نحن نؤمن بالحد الأدنى خاصة عندما يتعلق الأمر بكتابة التعليمات البرمجية. من ناحية أخرى ، كان بإمكاننا استخدام مكتبات مثل Express لكتابة تعليمات برمجية قابلة للتوسيع لهذا المشروع. ومع ذلك ، قررت غرائزنا بدء التشغيل نقل هذا وحفظه للمشروع التالي. أدوات إضافية استخدمناها:
- ioredis
هذه واحدة من المكتبات الأكثر دعمًا لاتصال Redis مع Node.js التي تستخدمها الشركات بما في ذلك Alibaba. - socket.io
أفضل مكتبة لاتصال رائع واحتياطي مع WebSocket و HTTP.
ريديس للتخزين العابر
موازين Redis كخدمة موثوقة للغاية وقابلة للتكوين. بالإضافة إلى أن هناك العديد من مزودي الخدمات المدارة الموثوق بهم لـ Redis ، بما في ذلك AWS. حتى إذا كنت لا ترغب في استخدام مزود ، فمن السهل البدء في Redis.
دعنا نكسر الجزء القابل للتكوين. لقد بدأنا بتكوين السيد والعبد المعتاد ، لكن Redis يأتي أيضًا مع أوضاع الكتلة أو الحارس. كل وضع له مزاياه الخاصة.
إذا تمكنا من مشاركة البيانات بطريقة ما ، فستكون مجموعة Redis هي الخيار الأفضل. ولكن إذا شاركنا البيانات من خلال أي استدلال ، فلدينا مرونة أقل حيث يجب اتباع الاستدلال عبر . قواعد أقل ، المزيد من التحكم مفيد للحياة!
يعمل Redis Sentinel بشكل أفضل بالنسبة لنا حيث يتم البحث عن البيانات في عقدة واحدة فقط ، والاتصال في نقطة زمنية معينة بينما لا يتم تجزئة البيانات. هذا يعني أيضًا أنه حتى في حالة فقد العديد من العقد ، تظل البيانات موزعة وموجودة في العقد الأخرى. لذلك لديك المزيد من HA وفرص أقل للخسارة. بالطبع ، أدى هذا إلى إزالة المحترفين من امتلاك مجموعة ، لكن حالة الاستخدام الخاصة بنا مختلفة.
العمارة على ارتفاع 30000 قدم
يوفر الرسم التخطيطي أدناه صورة عالية المستوى للغاية لكيفية عمل لوحات معلومات التشغيل الآلي والتطبيق. هل تتذكر نظام الوقت الفعلي الذي كان لدينا من القسم السابق؟

في الرسم التخطيطي الخاص بنا ، يتم تمييز سير العمل الرئيسي لدينا بحدود أكثر سمكًا. يتكون قسم "التشغيل الآلي" من:
- محطات
يتألف من الإصدارات الأصلية من Windows أو OSX أو Android أو iOS التي تحصل عليها أثناء الاختبار على BrowserStack. - مركز
نقطة الاتصال لجميع اختبارات السيلينيوم و Appium الخاصة بك باستخدام BrowserStack.
قسم "خدمة المستخدم" هنا هو حارس البوابة لدينا ، مما يضمن إرسال البيانات إلى الشخص المناسب وحفظها. إنه أيضًا حارس الأمن لدينا. يتضمن قسم "الدافع" جوهر ما ناقشناه في هذه المقالة. يتكون من المشتبه بهم المعتادين بما في ذلك:
- ريديس
تخزيننا المؤقت للرسائل ، حيث يتم تخزين السجلات الآلية مؤقتًا في حالتنا. - الناشر
هذا هو في الأساس الكيان الذي يحصل على البيانات من المحور. يتم التقاط جميع استجابات الطلبات الخاصة بك بواسطة هذا المكون الذي يكتب إلى Redis باستخدامsession_id
باعتبارها القناة. - مشترك
هذا يقرأ البيانات من Redis التي تم إنشاؤها من أجلsession_id
. وهو أيضًا خادم الويب للعملاء للاتصال عبر WebSocket (أو HTTP) للحصول على البيانات ثم إرسالها إلى العملاء المصادق عليهم.
أخيرًا ، لدينا قسم متصفح المستخدم ، والذي يمثل اتصال WebSocket مصادق عليه لضمان إرسال سجلات session_id
. يتيح ذلك للواجهة الأمامية JS تحليلها وتجميلها للمستخدمين.
على غرار خدمة السجلات ، لدينا هنا أداة دفع يتم استخدامها لتكاملات منتجات أخرى. بدلاً من session_id
، نستخدم نموذجًا آخر للمعرف لتمثيل هذه القناة. كل هذا يعمل من دافع!
الخلاصة (TLDR)
لقد حققنا نجاحًا كبيرًا في بناء Pub / Sub. لتلخيص سبب بنائه داخليًا:
- موازين أفضل لاحتياجاتنا ؛
- أرخص من خدمات الاستعانة بمصادر خارجية ؛
- سيطرة كاملة على الهيكل العام.
ناهيك عن أن JS هو الحل الأمثل لهذا النوع من السيناريوهات. ما تحتاجه المشكلة هو حلقة الأحداث والكمية الهائلة من الإدخال / الإخراج! جافا سكريبت هي سحر الخيط الزائف الفردي.
تعمل الأحداث و Redis كنظام على تبسيط الأمور للمطورين ، حيث يمكنك الحصول على البيانات من مصدر ودفعها إلى مصدر آخر عبر Redis. لذلك قمنا ببنائها.
إذا كان الاستخدام يناسب نظامك ، فإنني أوصي بفعل الشيء نفسه!