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

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

الخلفية

واجهة برمجة تطبيقات WebSocket

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

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

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

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

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

باستخدام واجهة برمجة تطبيقات 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. وهذا يعني أنه يمكن تطبيق الضغط "مجانًا" بدون أي تكلفة إضافية.

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

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

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

الوضع الحالي

| الخطوة | الحالة | | ------------------------------------------ | ---------------------------- | | 1. إنشاء رسالة توضيحية | [إكمال][الشرح] | | 2. إنشاء مسودة أولية للمواصفات | [قيد التقدم][المواصفات] | | 3. جمع الملاحظات وتكرارها بشأن التصميم | [قيد التقدّم](#feedback) | | 4. مرحلة التجربة الأصلية | [مكتمل][ot] | | 5. الإطلاق | لم يبدأ |

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

مثال تمهيدي

تعتمد واجهة برمجة التطبيقات 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. بالنسبة إلى إمكانية التشغيل التفاعلي مع واجهة 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 في مربع المكونات. تساهم ميزة Glitch في مشاركة حالات إعادة إنتاج سريعة وسهلة بشكل سريع.

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

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

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

روابط مفيدة

شكر وتقدير

تم تنفيذ واجهة برمجة التطبيقات WebSocketStream API من قِبل آدم Rice ويوتاكا هيرانو. صورة رئيسية من تصوير "دان موي" على قناة Unsplash