يمكنك منع تطبيقك من تلقّي عدد كبير من رسائل WebSocket أو إرسال عدد كبير من الرسائل إلى خادم WebSocket من خلال تطبيق الضغط الخلفي.
الخلفية
توفّر واجهة برمجة تطبيقات WebSocket واجهة JavaScript لبروتوكول WebSocket، ما يتيح فتح جلسة تفاعلية ثنائية الاتجاه بين متصفّح المستخدم والخادم. باستخدام واجهة برمجة التطبيقات هذه، يمكنك إرسال رسائل إلى خادم وتلقّي ردود مستندة إلى الأحداث بدون طلب الرد من الخادم بشكل متكرّر.
Streams API
تتيح واجهة برمجة التطبيقات 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()
على التعامل معها، ستؤدي عملية العرض إما إلى امتلاء الذاكرة من خلال تخزين تلك الرسائل مؤقتًا، أو إلى عدم الاستجابة بسبب استخدام وحدة المعالجة المركزية بنسبة% 100، أو إلى كليهما.
لا يُعدّ تطبيق الضغط الخلفي على الرسائل المُرسَلة أمرًا مريحًا.
يمكن تطبيق الضغط الخلفي على الرسائل المرسَلة، ولكن يتضمّن ذلك استطلاع قيمة السمة
WebSocket.bufferedAmount
،
وهو أمر غير فعّال وغير مريح.
تعرض هذه السمة للقراءة فقط عدد وحدات البايت من البيانات التي تم وضعها في قائمة الانتظار
باستخدام طلبات إلى
WebSocket.send()
،
ولكن لم يتم إرسالها بعد إلى الشبكة.
تتم إعادة ضبط هذه القيمة إلى صفر بعد إرسال جميع البيانات التي تم وضعها في قائمة الانتظار، ولكن إذا واصلت طلب WebSocket.send()
، ستستمر في الارتفاع.
ما هي واجهة برمجة التطبيقات WebSocketStream؟
تعالج واجهة برمجة التطبيقات WebSocketStream API مشكلة عدم توفّر ميزة "التحكّم في معدّل نقل البيانات" أو عدم ملاءمتها من خلال دمج عمليات البث مع واجهة برمجة التطبيقات WebSocket API. وهذا يعني أنّه يمكن تطبيق الضغط الخلفي "مجانًا"، بدون أي تكلفة إضافية.
حالات الاستخدام المقترَحة لواجهة برمجة التطبيقات WebSocketStream
في ما يلي أمثلة على المواقع الإلكترونية التي يمكنها استخدام واجهة برمجة التطبيقات هذه:
- تطبيقات WebSocket التي تتطلّب نطاقًا تردديًا عاليًا وتحتاج إلى الحفاظ على التفاعل، لا سيما تطبيقات الفيديو ومشاركة الشاشة
- وبالمثل، يمكن أن يؤدي التقاط الفيديو والتطبيقات الأخرى التي تنشئ الكثير من البيانات في المتصفح إلى الحاجة إلى تحميلها إلى الخادم. باستخدام ميزة "الضغط الخلفي"، يمكن للعميل التوقّف عن إنشاء البيانات بدلاً من تجميعها في الذاكرة.
الوضع الحالي
الخطوة | الحالة |
---|---|
1. إنشاء شرح | مكتمل |
2. إنشاء مسودة أولية للمواصفات | قيد التقدم |
3- جمع الملاحظات وتكرار التصميم | قيد التقدم |
4. مرحلة التجربة والتقييم | مكتمل |
5- إطلاق | لم تبدأ عملية المراجعة |
كيفية استخدام WebSocketStream API
تستند واجهة برمجة التطبيقات WebSocketStream إلى الوعود، ما يجعل التعامل معها يبدو طبيعيًا
في عالم 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
. لا تعمل AbortController.abort()
إلا قبل عملية تأكيد الاتصال، وليس بعدها.
const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);
يمكنك أيضًا استخدام طريقة WebSocketStream.close()
كبديل، ولكن الغرض الرئيسي منها هو السماح بتحديد الرمز والسبب الذي يتم إرساله إلى الخادم.
wss.close({closeCode: 4000, reason: 'Game over'});
التحسين التدريجي وإمكانية التشغيل التفاعلي
في الوقت الحالي، Chrome هو المتصفّح الوحيد الذي يتيح استخدام WebSocketStream API.
للتوافق مع واجهة برمجة التطبيقات WebSocket الكلاسيكية، لا يمكن تطبيق ضغط خلفي على الرسائل المستلَمة.
يمكن تطبيق الضغط الخلفي على الرسائل المرسَلة، ولكن يتضمّن ذلك استطلاع قيمة السمة
WebSocket.bufferedAmount
،
وهو أمر غير فعّال وغير مريح.
رصد الميزات
للتحقّق مما إذا كانت واجهة برمجة التطبيقات WebSocketStream متوافقة، استخدِم ما يلي:
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 | مصدر العرض التوضيحي لواجهة برمجة التطبيقات WebSocketStream
- خطأ في التتبُّع
- إدخال ChromeStatus.com
- مكوّن Blink:
Blink>Network>WebSockets
الإقرارات
تم تنفيذ WebSocketStream API بواسطة آدم رايس ويوتكا هيرانو.