WebSocketStream: دمج مجموعات البث مع واجهة برمجة تطبيقات WebSocket

يمكنك منع تطبيقك من الانغماس في رسائل WebSocket أو إغراق خادم WebSocket بالرسائل من خلال تطبيق الضغط العكسي.

الخلفية

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

واجهة برمجة التطبيقات Streams API

تتيح Streams API لJavaScript الوصول آليًا إلى مصادر مجموعات البيانات التي يتم استلامها من خلال الشبكة ومعالجتها على النحو المطلوب. يُعد الضغط الخلفي مفهومًا مهمًا في سياق أحداث البث المباشر. هذه هي العملية التي ينظم من خلالها تدفق واحد أو سلسلة أنبوبية سرعة القراءة أو الكتابة. عندما يكون البث نفسه أو بث لاحق في سلسلة المسار مشغولًا ولا يكون جاهزًا بعد لقبول المزيد من الأجزاء، يُرسِل إشارة إلى الوراء عبر السلسلة لإبطاء عملية التسليم حسب الاقتضاء.

مشكلة واجهة برمجة التطبيقات WebSocket API الحالية

من المستحيل تطبيق الضغط الخلفي على الرسائل المستلَمة.

باستخدام واجهة برمجة التطبيقات WebSocket API الحالية، يحدث التفاعل مع الرسالة في WebSocket.onmessage، EventHandler الذي يتم استدعاؤه عند تلقّي رسالة من الخادم.

لنفترض أن لديك تطبيقًا يحتاج إلى إجراء عمليات معالجة شديدة للبيانات عندما يتم تلقي رسالة جديدة. من المحتمل أن تقوم بإعداد التدفق بشكل مشابه للرمز أدناه، وبما أنك await نتيجة استدعاء process()، ينبغي أن تكون جيدًا، أليس كذلك؟

// A heavy data crunching operation.
const process = async (data) => {
  return new Promise((resolve) => {
    window.setTimeout(() => {
      console.log('WebSocket message processed:', data);
      return resolve('done');
    }, 1000);
  });
};

webSocket.onmessage = async (event) => {
  const data = event.data;
  // Await the result of the processing step in the message handler.
  await process(data);
};

خطأ! تكمن المشكلة في واجهة برمجة التطبيقات WebSocket API الحالية في عدم توفّر طريقة لتطبيق الضغط الخلفي. عندما تصل الرسائل بشكل أسرع من معالجتها بطريقة process()، ستملأ عملية العرض الذاكرة من خلال تخزينها مؤقتًا أو تصبح غير مستجيبة بسبب استخدام وحدة المعالجة المركزية (CPU) بنسبة% 100 أو كليهما.

إنّ تطبيق الضغط الخلفي على الرسائل المُرسَلة غير ملائم للاستخدام.

من الممكن تطبيق الضغط الخلفي على الرسائل المُرسَلة، ولكنّ ذلك يتطلّب الاستعلام عن السمة WebSocket.bufferedAmount ، وهي طريقة غير فعّالة وغير ملائمة. تعرض هذه السمة للقراءة فقط عدد وحدات البايت من البيانات التي تم وضعها في قائمة الانتظار باستخدام طلبات إلى WebSocket.send()، ولكن لم يتم إرسالها إلى الشبكة بعد. تتم إعادة ضبط هذه القيمة على الصفر بعد إرسال جميع البيانات في "قائمة الانتظار"، ولكن إذا واصلت طلب WebSocket.send()، ستواصل القيمة في الارتفاع.

ما هي واجهة برمجة التطبيقات WebSocketStream API؟

تعالج واجهة برمجة التطبيقات WebSocketStream API مشكلة الضغط الخلفي غير المتوفّر أو غير الملائم من خلال دمج أحداث البث مع واجهة برمجة التطبيقات WebSocket API. وهذا يعني أنّه يمكن تطبيق الضغط الخلفي "مجانًا"، بدون أي تكلفة إضافية.

حالات الاستخدام المقترَحة لواجهة برمجة التطبيقات WebSocketStream API

تشمل الأمثلة على المواقع الإلكترونية التي يمكنها استخدام واجهة برمجة التطبيقات هذه ما يلي:

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

الوضع الحالي

الخطوة الحالة
1. إنشاء فيديو توضيحي مكتمل
2. إنشاء مسودة أولية للمواصفة قيد التقدّم
3- جمع الملاحظات وتحسين التصميم قيد التقدّم
4. مرحلة التجربة والتقييم مكتمل
5- إطلاق لم تبدأ عملية المراجعة

كيفية استخدام واجهة برمجة التطبيقات WebSocketStream API

تستند واجهة برمجة التطبيقات WebSocketStream API إلى الوعود، ما يجعل التعامل معها تجربة طبيعية في عالم JavaScript الحديث. عليك البدء بإنشاء WebSocketStream جديد وتمريره عنوان URL لخادم WebSocket. بعد ذلك، عليك الانتظار إلى أن يصبح الاتصال opened، مما يؤدي إلى ظهور رمز ReadableStream و/أو رمز WritableStream.

من خلال استدعاء الطريقة ReadableStream.getReader()، يتم في النهاية الحصول على ReadableStreamDefaultReader، ويمكنك بعد ذلك read() البيانات منها إلى حين انتهاء البث المباشر، أي إلى أن يعرض كائنًا من النموذج {value: undefined, done: true}.

وفقًا لذلك، من خلال استدعاء الأسلوب WritableStream.getWriter() ، يمكنك الحصول أخيرًا على WritableStreamDefaultWriter، يمكنك بعد ذلك write() البيانات إليه.

  const wss = new WebSocketStream(WSS_URL);
  const {readable, writable} = await wss.opened;
  const reader = readable.getReader();
  const writer = writable.getWriter();

  while (true) {
    const {value, done} = await reader.read();
    if (done) {
      break;
    }
    const result = await process(value);
    await writer.write(result);
  }

الضغط العكسي

ماذا عن ميزة الضغط العكسي الموعودة؟ يمكنك الحصول عليه "مجانًا"، ولا يلزم اتّخاذ أي خطوات إضافية. إذا استغرقت process() وقتًا إضافيًا، لن يتم استخدام الرسالة التالية إلا بعد أن تصبح قناة الإرسال جاهزة. وبالمثل، لا يتم تنفيذ خطوة WritableStreamDefaultWriter.write() إلا إذا كان ذلك إجراءً آمنًا.

أمثلة متقدّمة

الوسيطة الثانية WebSocketStream هي عبارة عن حقيبة خيارات للسماح بتمديدها في المستقبل. الخيار الوحيد هو protocols، الذي يتصرف تمامًا مثل الوسيطة الثانية إلى الدالة الإنشائية WebSocket:

const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;

إنّ protocol المحدّد وextensions المحتمل هما جزء من القاموس المتاح من خلال WebSocketStream.opened. يتم توفير جميع المعلومات حول الاتصال المباشر من خلال هذا الوعد، لأنه ليست ذات صلة في حالة فشل الاتصال.

const {readable, writable, protocol, extensions} = await chatWSS.opened;

معلومات حول اتصال WebSocketStream المغلق

أصبحت المعلومات التي تم توفيرها من حدثَي WebSocket.onclose وWebSocket.onerror في WebSocket API متاحة الآن من خلال وعد WebSocketStream.closed. يتم رفض الوعد في حالة إغلاق غير نظيف، وإلا يؤدي إلى حل الرمز والسبب الذي أرسله الخادم.

يمكنك الاطّلاع على شرح لكل رموز الحالة المحتملة ومعانيها في قائمة رموز الحالة CloseEvent.

const {code, reason} = await chatWSS.closed;

إغلاق اتصال WebSocketStream

يمكن إغلاق WebSocketStream باستخدام AbortController. وبالتالي، مرِّر AbortSignal إلى الدالة الإنشائية WebSocketStream.

const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);

كبديل، يمكنك أيضًا استخدام الطريقة WebSocketStream.close()، ولكن الغرض الرئيسي منها هو السماح بتحديد الرمز والسبب الذي يتم إرساله إلى الخادم.

wss.close({code: 4000, reason: 'Game over'});

التحسين التدريجي والتشغيل التفاعلي

Chrome هو حاليًا المتصفّح الوحيد الذي يمكن من خلاله تنفيذ واجهة برمجة التطبيقات WebSocketStream API. بالنسبة إلى التشغيل التفاعلي باستخدام واجهة برمجة التطبيقات WebSocket API الكلاسيكية، لا يمكن تطبيق الضغط العكسي على الرسائل المُستلَمة. من الممكن تطبيق الضغط الخلفي على الرسائل المُرسَلة، ولكنّ ذلك يتطلّب الاستعلام عن السمة WebSocket.bufferedAmount ، وهي غير فعّالة وغير ملائمة.

رصد الميزات

للتحقّق مما إذا كانت واجهة برمجة التطبيقات WebSocketStream API متوافقة، يمكنك استخدام:

if ('WebSocketStream' in window) {
  // `WebSocketStream` is supported!
}

عرض توضيحي

في المتصفحات المتوافقة، يمكنك الاطّلاع على واجهة برمجة التطبيقات WebSocketStream API أثناء عملها في إطار iframe المضمّن، أو على Glitch مباشرةً.

ملاحظات

يريد فريق Chrome معرفة تجاربك مع واجهة برمجة التطبيقات WebSocketStream API.

أخبِرنا عن تصميم واجهة برمجة التطبيقات

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

الإبلاغ عن مشكلة في التنفيذ

هل واجهت مشكلة في التنفيذ في Chrome؟ أم أنّ عملية التنفيذ مختلفة عن المواصفات؟ يمكنك الإبلاغ عن خلل على الرابط new.crbug.com. احرص على تضمين أكبر قدر ممكن من التفاصيل وتعليمات بسيطة لإعادة إنتاج الخلل، وأدخِل Blink>Network>WebSockets في مربّع المكوّنات. تعمل ميزة الخطأ بشكلٍ رائع لمشاركة حافظات إعادة الإنتاج السريعة والسهلة.

إظهار الدعم لواجهة برمجة التطبيقات

هل لديك خطة لاستخدام واجهة برمجة التطبيقات WebSocketStream API؟ يساعد دعمك العلني فريق Chrome في تحديد أولويات الميزات، ويُظهر لموفّري المتصفّحات الآخرين مدى أهمية توفير هذه الميزات.

يمكنك إرسال تغريدة إلى @ChromiumDev باستخدام علامة التصنيف #WebSocketStream وإعلامنا بمكان استخدامها وكيفية استخدامها.

روابط مفيدة

الشكر والتقدير

نفّذ آدم رايس ويوتاكا هيرانو واجهة برمجة التطبيقات WebSocketStream API.