استخدام SSE بدلاً من WebSockets لتدفق البيانات أحادي الاتجاه عبر HTTP / 2

نشرت: 2022-03-10
ملخص سريع ↬ في أي وقت نقوم فيه بتصميم تطبيق ويب باستخدام بيانات في الوقت الفعلي ، نحتاج إلى التفكير في كيفية قيامنا بتسليم بياناتنا من الخادم إلى العميل. عادةً ما تكون الإجابة الافتراضية هي "WebSockets". لكن هل يوجد طريق افضل؟ دعنا نقارن ثلاث طرق مختلفة: الاقتراع الطويل ، ومقابس الويب ، والأحداث المرسلة من الخادم ؛ لفهم قيودهم في العالم الحقيقي. الجواب قد يفاجئك.

عند إنشاء تطبيق ويب ، يجب على المرء أن يفكر في نوع آلية التسليم التي سيستخدمها. لنفترض أن لدينا تطبيقًا متعدد الأنظمة الأساسية يعمل مع البيانات في الوقت الفعلي ؛ تطبيق سوق الأوراق المالية يوفر القدرة على شراء أو بيع الأسهم في الوقت الفعلي. يتكون هذا التطبيق من عناصر واجهة مستخدم تقدم قيمة مختلفة للمستخدمين المختلفين.

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

في الوقت الحاضر ، هناك عدة طرق لتنفيذ ما يلي:

  • الاقتراع الطويل / القصير (سحب العميل)
  • WebSockets (دفع الخادم)
  • الأحداث المرسلة من الخادم (دفع الخادم).

سنلقي نظرة متعمقة على البدائل الثلاثة بعد أن حددنا متطلبات دراسة الجدوى الخاصة بنا.

حالة العمل

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

المزيد بعد القفز! أكمل القراءة أدناه ↓

المأزق الرئيسي هنا هو أن عدد الاتصالات سيزداد خطيًا مع عدد الأدوات المصغّرة لدينا وسنصل إلى حد المتصفحات لعدد طلبات HTTP التي يتم التعامل معها في وقت واحد.

تتكون البيانات التي ستتلقاها أدواتنا بشكل أساسي من أرقام وتحديثات لأرقامها: تحتوي الاستجابة الأولية على عشرة أسهم مع بعض القيم السوقية لها. يتضمن ذلك تحديثات إضافة / إزالة الأسهم وتحديثات القيم السوقية للمخزونات المعروضة حاليًا. ننقل كميات صغيرة من سلاسل JSON لكل تحديث بأسرع ما يمكن.

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

  • سنستخدم NGINX لموازنة الحمل والوكيل لإخفاء جميع نقاط النهاية لدينا خلف نفس المجال. سيمكننا ذلك من استخدام تعدد إرسال HTTP / 2 خارج الصندوق.
  • نريد استخدام شبكة الأجهزة المحمولة والبطارية بكفاءة.

البدائل

الاقتراع الطويل

الاقتراع الطويل

سحب العميل هو تطبيق مكافئ لطفل مزعج يجلس في المقعد الخلفي لسيارتك يسأل باستمرار ، "هل نحن هناك بعد؟" باختصار ، يطلب العميل من الخادم البيانات. الخادم ليس لديه بيانات وينتظر بعض الوقت قبل إرسال الاستجابة:

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

تعمل مكالمات AJAX على بروتوكول HTTP مما يعني أن الطلبات إلى نفس المجال يجب أن تتضاعف بشكل افتراضي. ومع ذلك ، فقد واجهنا عدة مشكلات في محاولة جعل ذلك يعمل على النحو المطلوب. بعض المزالق التي حددناها في نهج التطبيقات المصغّرة لدينا:

  • رؤوس علوية
    كل طلب واستجابة لاستطلاع هو رسالة HTTP كاملة وتحتوي على مجموعة كاملة من رؤوس HTTP في تأطير الرسالة. في حالتنا حيث لدينا رسائل متكررة صغيرة ، تمثل الرؤوس في الواقع النسبة الأكبر من البيانات المرسلة. الحمولة الفعلية المفيدة أقل بكثير من إجمالي البايت المرسلة (على سبيل المثال ، 15 كيلوبايت من الرؤوس لـ 5 كيلوبايت من البيانات).

  • الكمون الأقصى
    بعد أن يستجيب الخادم ، لم يعد بإمكانه إرسال البيانات إلى العميل حتى يرسل العميل الطلب التالي. في حين أن متوسط ​​زمن الوصول للاستقصاء الطويل يقترب من عبور شبكة واحدة ، فإن الحد الأقصى لوقت الاستجابة يتجاوز ثلاث عمليات نقل للشبكة: الاستجابة ، والطلب ، والاستجابة. ومع ذلك ، نظرًا لفقدان الحزمة وإعادة الإرسال ، فإن أقصى زمن انتقال لأي بروتوكول TCP / IP سيكون أكثر من ثلاث عمليات نقل للشبكة (يمكن تجنبها باستخدام خطوط أنابيب HTTP). بينما في اتصال LAN المباشر ، هذه ليست مشكلة كبيرة ، فهي تصبح واحدة أثناء التنقل وتبديل خلايا الشبكة. إلى حد ما ، يتم ملاحظة ذلك مع SSE و WebSockets ، ولكن التأثير يكون أكبر مع الاقتراع.

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

  • انحطاط الأداء
    يميل عميل الاستقصاء الطويل (أو الخادم) الذي يخضع للحمل بشكل طبيعي إلى التدهور في الأداء على حساب زمن انتقال الرسالة. عندما يحدث ذلك ، فإن الأحداث التي يتم دفعها إلى العميل ستنتظر في قائمة الانتظار. هذا يعتمد حقًا على التنفيذ ؛ في حالتنا ، نحتاج إلى تجميع جميع البيانات لأننا نرسل أحداث إضافة / إزالة / تحديث إلى عناصر واجهة المستخدم الخاصة بنا.

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

  • مضاعفة
    يمكن أن يحدث هذا إذا حدثت الاستجابات في نفس الوقت عبر اتصال HTTP / 2 مستمر. قد يكون هذا خادعًا لأن ردود الاقتراع لا يمكن أن تكون متزامنة حقًا.

يمكن العثور على المزيد حول قضايا العالم الحقيقي التي يمكن للمرء أن يواجهها مع الاقتراع الطويل هنا .

مآخذ الويب

مآخذ الويب

كمثال أول على طريقة دفع الخادم ، سنلقي نظرة على WebSockets.

عبر MDN:

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

هذا بروتوكول اتصالات يوفر قنوات اتصال ثنائية الاتجاه عبر اتصال TCP واحد.

يقع كل من HTTP و WebSockets في طبقة التطبيق من نموذج OSI وبالتالي يعتمدان على TCP في الطبقة 4.

  1. تطبيق
  2. عرض تقديمي
  3. جلسة
  4. المواصلات
  5. شبكة الاتصال
  6. وصلة البيانات
  7. جسدي - بدني

ينص RFC 6455 على أن WebSocket "مصمم للعمل عبر منافذ HTTP 80 و 443 بالإضافة إلى دعم وكلاء ووسطاء HTTP" مما يجعله متوافقًا مع بروتوكول HTTP. لتحقيق التوافق ، تستخدم مصافحة WebSocket رأس ترقية HTTP للتغيير من بروتوكول HTTP إلى بروتوكول WebSocket.

هناك أيضًا مقال جيد جدًا يشرح كل ما تحتاج لمعرفته حول WebSockets على ويكيبيديا. أنا أشجعكم على قراءتها.

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

  • الخوادم الوكيلة : بشكل عام ، هناك بعض المشكلات المختلفة في WebSockets والوكلاء:

    • الأول يتعلق بمزودي خدمة الإنترنت والطريقة التي يتعاملون بها مع شبكاتهم. قامت المشكلات المتعلقة بوكلاء نصف القطر بإغلاق المنافذ ، وما إلى ذلك.
    • يتعلق النوع الثاني من المشكلات بالطريقة التي يتم بها تكوين الوكيل للتعامل مع حركة مرور HTTP غير الآمنة والاتصالات طويلة العمر (يتم تقليل التأثير باستخدام HTTPS).
    • المشكلة الثالثة "مع WebSockets ، فأنت مجبر على تشغيل بروكسيات TCP بدلاً من بروكسيات HTTP. لا يمكن لبروكسيات بروتوكول التحكم في الإرسال (TCP) حقن الرؤوس أو إعادة كتابة عناوين URL أو أداء العديد من الأدوار التي نتركها تقليديًا لبروكسيات HTTP الخاصة بنا ".
  • عدد من الاتصالات : لا ينطبق حد الاتصال الشهير لطلبات HTTP والذي يدور حول الرقم 6 على WebSockets. 50 مقبس = 50 وصلة. عشر علامات تبويب في المتصفح بمقدار 50 مقبس = 500 اتصال وما إلى ذلك. نظرًا لأن WebSocket هو بروتوكول مختلف لتقديم البيانات ، فإنه لا يتم مضاعفة إرساله تلقائيًا عبر اتصالات HTTP / 2 (لا يعمل بالفعل فوق HTTP على الإطلاق). يعد تنفيذ مضاعفة الإرسال المخصص على كل من الخادم والعميل أمرًا معقدًا للغاية بحيث لا يجعل المقابس مفيدة في حالة العمل المحددة. علاوة على ذلك ، يقرن هذا أدواتنا بمنصتنا لأنها ستحتاج إلى نوع من واجهة برمجة التطبيقات على العميل للاشتراك فيها ولا يمكننا توزيعها بدونها.

  • موازنة الحمل (بدون مضاعفة) : إذا فتح كل n عددًا من المقابس ، فإن موازنة الحمل المناسبة معقدة للغاية. عندما يتم تحميل خوادمك بشكل زائد ، وتحتاج إلى إنشاء مثيلات جديدة وإنهاء مثيلات قديمة اعتمادًا على تنفيذ برنامجك ، يمكن أن تؤدي الإجراءات التي يتم اتخاذها عند "إعادة الاتصال" إلى تشغيل سلسلة ضخمة من التحديثات والطلبات الجديدة للبيانات التي ستؤدي إلى زيادة التحميل على نظامك . يجب صيانة WebSockets على كل من الخادم والعميل. لا يمكن نقل اتصالات المقبس إلى خادم مختلف إذا كان الخادم الحالي يواجه حمولة عالية. يجب إغلاقها وإعادة فتحها.

  • DoS : يتم التعامل مع هذا عادةً بواسطة بروكسيات HTTP الأمامية التي لا يمكن معالجتها بواسطة بروكسيات TCP المطلوبة لـ WebSockets. يمكن للمرء الاتصال بالمقبس ويبدأ في إغراق الخوادم بالبيانات. تجعلك WebSockets عرضة لهذا النوع من الهجمات.

  • إعادة اختراع العجلة : باستخدام WebSockets ، يجب على المرء أن يتعامل مع الكثير من المشكلات التي يتم التعامل معها في HTTP بمفرده.

يمكن قراءة المزيد حول مشكلات العالم الحقيقي مع WebSockets هنا.

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

تأثير

نحصل على زيادة في النفقات التشغيلية من حيث التطوير والاختبار والتوسع ؛ البرنامج والبنية التحتية لتكنولوجيا المعلومات الخاصة به مع كل من: الاقتراع و WebSockets.

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

ولكن لماذا ما زلنا نواجه مشكلات مع الأجهزة المحمولة؟

لنفكر في كيفية اتصال الجهاز المحمول الافتراضي بالإنترنت:

تحتاج الأجهزة المحمولة إلى المرور ببعض الحلقات قبل أن تتمكن من الاتصال بالإنترنت.

شرح مباشر لكيفية عمل شبكة الهاتف المحمول: عادةً ما تحتوي الأجهزة المحمولة على هوائي منخفض الطاقة قد يستقبل البيانات من خلية. بهذه الطريقة ، بمجرد أن يستقبل الجهاز البيانات من مكالمة واردة ، يقوم بتشغيل هوائي مزدوج الاتجاه من أجل إجراء المكالمة. يتم استخدام نفس الهوائي متى أردت إجراء مكالمة أو الوصول إلى الإنترنت (في حالة عدم توفر شبكة WiFi). يحتاج الهوائي مزدوج الاتجاه إلى إنشاء اتصال بالشبكة الخلوية والقيام ببعض المصادقة. بمجرد إنشاء الاتصال ، هناك بعض الاتصالات بين جهازك والخلية من أجل تنفيذ طلب الشبكة الخاص بنا. تتم إعادة توجيهنا إلى الوكيل الداخلي لمزود خدمة الهاتف المحمول الذي يتعامل مع طلبات الإنترنت. من الآن فصاعدًا ، الإجراء معروف بالفعل: يسأل DNS حيث يكون www.domainname.ext في الواقع ، يتلقى URI إلى المورد ، ويتم إعادة توجيهه في النهاية إليه.

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

بدون WiFi ، يتطلب كل من WebSockets والاستقصاء هوائي مزدوج الاتجاه للعمل بشكل مستمر تقريبًا. وبالتالي ، فإننا نواجه زيادة في استهلاك البيانات وزيادة في سحب الطاقة - واعتمادًا على الجهاز - الحرارة أيضًا.

بحلول الوقت الذي تبدو فيه الأمور قاتمة ، يبدو أننا سنضطر إلى إعادة النظر في متطلبات العمل لتطبيقنا. هل نفتقد شيئا؟

SSE

الأحداث المرسلة من الخادم

عبر MDN:

“يتم استخدام واجهة EventSource لتلقي الأحداث المرسلة من الخادم. يتصل بخادم عبر HTTP ويستقبل الأحداث بتنسيق نص / حدث بث دون إغلاق الاتصال. "

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

عبر html5doctor.com:

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

يبدو الأمر غريبًا نوعًا ما ، ولكن بعد التفكير - تدفق البيانات الرئيسي لدينا هو من الخادم إلى العميل وفي حالات أقل بكثير من العميل إلى الخادم.

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

خصائص فريدة

  • يأتي دفق الاتصال من الخادم وهو للقراءة فقط.
  • يستخدمون طلبات HTTP العادية للاتصال المستمر ، وليس بروتوكولًا خاصًا. الحصول على مضاعفة إرسال عبر HTTP / 2 خارج منطقة الجزاء.
  • في حالة انقطاع الاتصال ، يقوم EventSource بإطلاق حدث خطأ ويحاول إعادة الاتصال تلقائيًا. يمكن للخادم أيضًا التحكم في المهلة قبل أن يحاول العميل إعادة الاتصال (موضح بمزيد من التفاصيل لاحقًا).
  • يمكن للعملاء إرسال معرف فريد مع الرسائل. عندما يحاول العميل إعادة الاتصال بعد انقطاع الاتصال ، سيرسل آخر معرف معروف. ثم يمكن للخادم أن يرى أن العميل قد فاته عدد n من الرسائل وإرسال تراكم الرسائل الفائتة عند إعادة الاتصال.

نموذج تنفيذ العميل

تشبه هذه الأحداث أحداث JavaScript العادية التي تحدث في المتصفح - مثل أحداث النقر - باستثناء أنه يمكننا التحكم في اسم الحدث والبيانات المرتبطة به.

دعنا نرى معاينة التعليمات البرمجية البسيطة من جانب العميل:

 // subscribe for messages var source = new EventSource('URL'); // handle messages source.onmessage = function(event) { // Do something with the data: event.data; };

ما نراه من المثال هو أن جانب العميل بسيط إلى حد ما. يتصل بمصدرنا وينتظر تلقي الرسائل.

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

يبدو تنفيذ العميل لـ WebSockets مشابه جدًا لهذا. يكمن التعقيد مع المقابس في البنية التحتية لتكنولوجيا المعلومات وتنفيذ الخادم.

EventSource

يحتوي كل كائن EventSource على الأعضاء التاليين:

  • URL: تعيين أثناء البناء.
  • الطلب: في البداية باطل.
  • وقت إعادة الاتصال: القيمة بالمللي ثانية (القيمة التي يحددها وكيل المستخدم).
  • معرف الحدث الأخير: سلسلة فارغة في البداية.
  • حالة الاستعداد: حالة الاتصال.
    • توصيل (0)
    • مفتوح (1)
    • مغلق (2)

بصرف النظر عن عنوان URL ، يتم التعامل مع جميعها على أنها خاصة ولا يمكن الوصول إليها من الخارج.

أحداث البناء:

  1. افتح
  2. رسالة
  3. خطأ

معالجة قطرات الاتصال

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

نموذج تنفيذ الخادم

حسنًا ، إذا كان العميل بهذه البساطة ، فربما يكون تنفيذ الخادم معقدًا؟

حسنًا ، قد يبدو معالج الخادم لـ SSE كما يلي:

 function handler(response) { // setup headers for the response in order to get the persistent HTTP connection response.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // compose the message response.write('id: UniqueID\n'); response.write("data: " + data + '\n\n'); // whenever you send two new line characters the message is sent automatically }

نحدد الوظيفة التي ستتعامل مع الاستجابة:

  1. رؤوس الإعداد
  2. إنشاء رسالة
  3. إرسال

لاحظ أنك لا ترى استدعاء طريقة send() أو push() . هذا لأن المعيار يحدد أنه سيتم إرسال الرسالة بمجرد تلقيها حرفين \n\n كما في المثال: response.write("data: " + data + '\n\n'); . سيؤدي هذا على الفور إلى دفع الرسالة إلى العميل. يرجى ملاحظة أن data يجب أن تكون سلسلة أحرف لا تحتوي على أحرف سطر جديد في نهايتها.

بناء الرسالة

كما ذكرنا سابقًا ، يمكن أن تحتوي الرسالة على بعض الخصائص:

  1. هوية شخصية
    إذا كانت قيمة الحقل لا تحتوي على U + 0000 NULL ، فقم بتعيين المخزن المؤقت لمعرف الحدث الأخير على قيمة الحقل. خلاف ذلك ، تجاهل الحقل.
  2. بيانات
    قم بإلحاق قيمة الحقل بمخزن البيانات المؤقت ، ثم قم بإلحاق حرف U + 000A LINE FEED (LF) واحد بمخزن البيانات المؤقت.
  3. هدف
    اضبط المخزن المؤقت لنوع الحدث على قيمة الحقل. هذا يؤدي إلى event.type الحصول على اسم الحدث المخصص الخاص بك.
  4. أعد المحاولة
    إذا كانت قيمة الحقل تتكون من أرقام ASCII فقط ، فسر قيمة الحقل على أنها عدد صحيح في الأساس عشرة ، وقم بتعيين وقت إعادة اتصال تيار الحدث إلى هذا العدد الصحيح. خلاف ذلك ، تجاهل الحقل.

سيتم تجاهل أي شيء آخر. لا يمكننا تقديم مجالاتنا الخاصة.

مثال مع event مضاف:

 response.write('id: UniqueID\n'); response.write('event: add\n'); response.write('retry: 10000\n'); response.write("data: " + data + '\n\n');

ثم على العميل ، يتم التعامل مع هذا الأمر مع addEventListener على هذا النحو:

 source.addEventListener("add", function(event) { // do stuff with data event.data; });

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

 ... id: 54 event: add data: "[{SOME JSON DATA}]" id: 55 event: remove data: JSON.stringify(some_data) id: 56 event: remove data: { data: "msg" : "JSON data"\n data: "field": "value"\n data: "field2": "value2"\n data: }\n\n ...

هذا يبسط إلى حد كبير ما يمكننا فعله ببياناتنا.

متطلبات الخادم المحددة

خلال نقطة الوصول (POC) الخاصة بنا للنهاية الخلفية ، حددنا أنها تحتوي على بعض التفاصيل التي يجب معالجتها للحصول على تنفيذ عملي لـ SSE. أفضل سيناريو ستستخدمه هو خادم قائم على حلقة الأحداث مثل NodeJS أو Kestrel أو Twisted. الفكرة هي أنه مع الحل القائم على الخيط ، سيكون لديك مؤشر ترابط لكل اتصال → 1000 اتصال = 1000 موضوع. مع حل حلقة الحدث ، سيكون لديك موضوع واحد لكل 1000 اتصال.

  1. يمكنك فقط قبول طلبات EventSource إذا نص طلب HTTP على أنه يمكنه قبول نوع تدفق الأحداث MIME ؛
  2. تحتاج إلى الاحتفاظ بقائمة بجميع المستخدمين المتصلين من أجل إرسال أحداث جديدة ؛
  3. يجب الاستماع إلى الاتصالات التي تم إسقاطها وإزالتها من قائمة المستخدمين المتصلين ؛
  4. يجب أن تحتفظ اختياريًا بسجل للرسائل حتى تتمكن إعادة الاتصال بالعملاء من اللحاق بالرسائل الفائتة.

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

  • من المعروف أن الخوادم الوكيلة القديمة ، في حالات معينة ، تسقط اتصالات HTTP بعد مهلة قصيرة. للحماية من هذه الخوادم الوكيلة ، يمكن للمؤلفين تضمين سطر تعليق (سطر يبدأ بحرف ":") كل 15 ثانية أو نحو ذلك.

  • قد يجد المؤلفون الذين يرغبون في ربط اتصالات مصدر الحدث ببعضهم البعض أو بمستندات معينة تم تقديمها مسبقًا أن الاعتماد على عناوين IP لا يعمل ، حيث يمكن أن يكون للعملاء الفرديين عناوين IP متعددة (بسبب وجود خوادم وكيلة متعددة) ويمكن أن يكون لعناوين IP الفردية عدة عملاء (بسبب مشاركة خادم وكيل). من الأفضل تضمين معرف فريد في المستند عند تقديمه ثم تمرير هذا المعرف كجزء من عنوان URL عند إنشاء الاتصال.

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

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

  • دعم المستعرض و Polyfills: تتخلف Edge عن هذا التنفيذ ، ولكن يتوفر polyfill يمكن أن يوفر لك. ومع ذلك ، فإن أهم حالة لـ SSE هي للأجهزة المحمولة حيث لا تمتلك IE / Edge حصة سوقية قابلة للتطبيق.

بعض polyfill المتاحة:

  • يافل
  • أمفتك
  • ريمي

دفع بدون اتصال وميزات أخرى

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

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

  1. يتصل المتصفح بخادم HTTP بعيد ويطلب المورد المحدد بواسطة المؤلف في مُنشئ EventSource.
  2. يرسل الخادم رسائل عرضية.
  3. بين رسالتين ، يكتشف المستعرض أنه خامل باستثناء نشاط الشبكة المتضمن في الحفاظ على اتصال TCP قيد التشغيل ، ويقرر التبديل إلى وضع السكون لتوفير الطاقة.
  4. المستعرض قطع الاتصال بالخادم.
  5. يتصل المتصفح بخدمة على الشبكة ويطلب أن تحافظ الخدمة ، "وكيل الدفع" ، على الاتصال بدلاً من ذلك.
  6. تتصل خدمة "وكيل الدفع" بخادم HTTP البعيد وتطلب المورد المحدد من قبل المؤلف في مُنشئ EventSource (ربما يتضمن رأس HTTP Last-Event-ID ، وما إلى ذلك).
  7. يسمح المتصفح للجهاز المحمول بالنوم.
  8. يرسل الخادم رسالة أخرى.
  9. تستخدم خدمة "push proxy" تقنية مثل دفع OMA لنقل الحدث إلى الجهاز المحمول ، والذي يستيقظ بما يكفي فقط لمعالجة الحدث ثم العودة إلى وضع السكون.

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

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

ملخص

بعد نقاط الوصول الطويلة والشاملة بما في ذلك تطبيقات الخادم والعميل ، يبدو أن SSE هو الحل لمشاكلنا في تسليم البيانات. هناك بعض المزالق معها أيضًا ، لكن ثبت أنها تافهة لإصلاحها.

هكذا يبدو إعداد الإنتاج لدينا في النهاية:

نظرة عامة على العمارة
نظرة عامة على العمارة النهائية. تقع جميع نقاط نهاية API خلف nginx بحيث يحصل العملاء على استجابة مضاعفة.

نحصل على ما يلي من NGINX:

  • وكيل لنقاط نهاية API في أماكن مختلفة ؛
  • HTTP / 2 وجميع فوائده مثل مضاعفة الاتصالات ؛
  • توزيع الحمل؛
  • SSL.

بهذه الطريقة ندير تسليم البيانات والشهادات الخاصة بنا في مكان واحد بدلاً من القيام بذلك في كل نقطة نهاية على حدة.

الفوائد الرئيسية التي نحصل عليها من هذا النهج هي:

  • كفاءة البيانات ؛
  • تنفيذ أبسط
  • يتم مضاعفة الإرسال تلقائيًا عبر HTTP / 2 ؛
  • يحد من عدد اتصالات البيانات على العميل بواحد ؛
  • يوفر آلية لحفظ البطارية عن طريق إلغاء تحميل الاتصال ببروكسي.

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

تحقق من العرض التوضيحي للشفرة إذا كنت ترغب في الحصول على تطبيق خادم عميل بسيط.

موارد

  • "المشكلات المعروفة وأفضل الممارسات لاستخدام الاقتراع الطويل والبث المباشر في HTTP ثنائي الاتجاه ،" IETF (PDF)
  • توصية W3C ، W3C
  • "هل ستنجو WebSocket من HTTP / 2 ؟،" ألان دينيس ، InfoQ
  • "دفق التحديثات مع الأحداث المرسلة من الخادم" ، إريك بيدلمان ، HTML5 Rocks
  • "تطبيقات دفع البيانات باستخدام HTML5 SSE" دارين كوك ، أورايلي ميديا