با اعمال فشار برگشتی، از غرق شدن برنامه خود در پیامهای 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()
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 نسخه ی نمایشی | منبع آزمایشی WebSocketStream API
- اشکال ردیابی
- ورودی ChromeStatus.com
- Blink Component:
Blink>Network>WebSockets
قدردانی ها
WebSocketStream API توسط آدام رایس و یوتاکا هیرانو پیاده سازی شد. تصویر قهرمان توسط Daan Mooij در Unsplash .