WebSocketStream: یکپارچه سازی جریان ها با WebSocket API، WebSocketStream: یکپارچه سازی جریان ها با WebSocket API

با اعمال فشار برگشتی، از غرق شدن برنامه خود در پیام‌های WebSocket یا پر کردن پیام‌ها به سرور WebSocket جلوگیری کنید.

زمینه

WebSocket API

WebSocket API یک رابط جاوا اسکریپت برای پروتکل WebSocket فراهم می کند که امکان باز کردن یک جلسه ارتباط تعاملی دو طرفه بین مرورگر کاربر و سرور را فراهم می کند. با استفاده از این API، می‌توانید پیام‌هایی را به سرور ارسال کنید و پاسخ‌های رویداد محور را بدون نظرسنجی از سرور برای پاسخ دریافت کنید.

Streams API

Streams API به جاوا اسکریپت اجازه می دهد تا به صورت برنامه نویسی به جریان های تکه های داده دریافتی از طریق شبکه دسترسی داشته باشد و آنها را به دلخواه پردازش کند. یک مفهوم مهم در زمینه جریان ها، فشار برگشتی است. این فرآیندی است که طی آن یک جریان یا یک زنجیره لوله سرعت خواندن یا نوشتن را تنظیم می کند. هنگامی که خود جریان یا جریان بعدی در زنجیره لوله هنوز مشغول است و هنوز آماده پذیرش تکه های بیشتر نیست، سیگنالی را از طریق زنجیره به عقب می فرستد تا تحویل را در صورت لزوم کاهش دهد.

مشکل با 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() می‌رسند، فرآیند رندر یا با بافر کردن آن پیام‌ها، حافظه را پر می‌کند، یا به دلیل استفاده 100% از CPU پاسخگو نمی‌شود یا هر دو.

اعمال فشار برگشتی برای پیام های ارسالی غیر ارگونومیک است

اعمال فشار برگشتی برای پیام‌های ارسالی امکان‌پذیر است، اما شامل بررسی ویژگی WebSocket.bufferedAmount است که ناکارآمد و غیر ارگونومیک است. این ویژگی فقط خواندنی، تعداد بایت‌های داده‌ای را که با استفاده از تماس‌های WebSocket.send() در صف قرار گرفته‌اند، اما هنوز به شبکه منتقل نشده‌اند، برمی‌گرداند. این مقدار پس از ارسال تمام داده های صف به صفر بازنشانی می شود، اما اگر به فراخوانی WebSocket.send() ادامه دهید، به بالا رفتن ادامه خواهد داد.

WebSocketStream API چیست؟

WebSocketStream API با مشکل عدم وجود یا غیر ارگونومیک فشار برگشتی با ادغام جریان ها با WebSocket API سروکار دارد. این بدان معنی است که فشار برگشتی را می توان "رایگان" و بدون هیچ هزینه اضافی اعمال کرد.

موارد استفاده پیشنهادی برای WebSocketStream API

نمونه هایی از سایت هایی که می توانند از این API استفاده کنند عبارتند از:

  • برنامه های WebSocket با پهنای باند بالا که نیاز به حفظ تعامل دارند، به ویژه اشتراک گذاری ویدیو و صفحه نمایش.
  • به طور مشابه، ضبط ویدیو و سایر برنامه‌هایی که داده‌های زیادی را در مرورگر تولید می‌کنند که باید در سرور آپلود شوند. با فشار برگشتی، مشتری می تواند به جای انباشت داده در حافظه، تولید داده را متوقف کند.

وضعیت فعلی

گام وضعیت
1. توضیح دهنده ایجاد کنید کامل
2. پیش نویس اولیه مشخصات را ایجاد کنید در حال پیش رفت
3. جمع آوری بازخورد و تکرار در طراحی در حال پیش رفت
4. آزمایش مبدا کامل
5. راه اندازی کنید شروع نشده است

نحوه استفاده از WebSocketStream API

مثال مقدماتی

WebSocketStream API مبتنی بر وعده است، که باعث می شود در دنیای مدرن جاوا اسکریپت، برخورد با آن طبیعی باشد. شما با ساختن یک WebSocketStream جدید و ارسال URL سرور WebSocket به آن شروع می کنید. سپس، منتظر می‌مانید تا اتصال opened شود، که منجر به ReadableStream و/یا WritableStream می‌شود.

با فراخوانی متد ReadableStream.getReader() ، در نهایت یک ReadableStreamDefaultReader به دست می آورید، که سپس می توانید داده های آن read() ، یعنی تا زمانی که یک شی به شکل {value: undefined, done: true} برگرداند. {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);
  }

فشار پشت

در مورد ویژگی backpressure وعده داده شده چطور؟ همانطور که در بالا نوشتم، شما آن را "رایگان" دریافت می کنید، بدون نیاز به مراحل اضافی. اگر 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'});

بهبود پیشرونده و قابلیت همکاری

کروم در حال حاضر تنها مرورگری است که WebSocketStream API را پیاده سازی می کند. برای همکاری با WebSocket API کلاسیک، اعمال فشار برگشتی برای پیام های دریافتی امکان پذیر نیست. اعمال فشار برگشتی برای پیام‌های ارسالی امکان‌پذیر است، اما شامل بررسی ویژگی WebSocket.bufferedAmount است که ناکارآمد و غیر ارگونومیک است.

تشخیص ویژگی

برای بررسی اینکه آیا WebSocketStream API پشتیبانی می‌شود، از این موارد استفاده کنید:

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

نسخه ی نمایشی

در مرورگرهای پشتیبانی کننده، می‌توانید WebSocketStream API را در iframe تعبیه‌شده یا مستقیماً در Glitch مشاهده کنید.

بازخورد

تیم Chrome می‌خواهد درباره تجربیات شما با WebSocketStream API بشنود.

در مورد طراحی API به ما بگویید

آیا چیزی در مورد API وجود دارد که آنطور که انتظار داشتید کار نمی کند؟ یا آیا روش ها یا ویژگی هایی وجود دارد که برای اجرای ایده خود به آنها نیاز دارید؟ سوال یا نظری در مورد مدل امنیتی دارید؟ یک مشکل مشخصات را در مخزن GitHub مربوطه ثبت کنید یا افکار خود را به یک مشکل موجود اضافه کنید.

گزارش مشکل در اجرا

آیا اشکالی در پیاده سازی کروم پیدا کردید؟ یا اجرا با مشخصات متفاوت است؟ یک اشکال را در new.crbug.com ثبت کنید. اطمینان حاصل کنید که تا جایی که می توانید جزئیات، دستورالعمل های ساده برای بازتولید را وارد کنید و Blink>Network>WebSockets در کادر Components وارد کنید. Glitch برای به اشتراک گذاری موارد بازتولید سریع و آسان عالی عمل می کند.

پشتیبانی از API را نشان دهید

آیا قصد دارید از WebSocketStream API استفاده کنید؟ پشتیبانی عمومی شما به تیم Chrome کمک می‌کند ویژگی‌ها را اولویت‌بندی کند و به سایر فروشندگان مرورگر نشان می‌دهد که چقدر حمایت از آنها ضروری است.

با استفاده از هشتگ #WebSocketStream یک توییت به ChromiumDev@ ارسال کنید و به ما اطلاع دهید که کجا و چگونه از آن استفاده می کنید.

لینک های مفید

سپاسگزاریها

WebSocketStream API توسط آدام رایس و یوتاکا هیرانو پیاده سازی شد. تصویر قهرمان توسط Daan Mooij در Unsplash .