JS (браузър + Node.js): Broadcast Channel API — Как да намалим натоварването на сървъра

Епиграф

И той им каза: „Идете по целия свят и прогласете...“ ―Марк 16:15

проблем

Имате приложение и всеки раздел на браузъра има отделен негов екземпляр. Всеки екземпляр може независимо да обработва връзки, да изпълнява заявки, да слуша изпратени от сървъра събития и т.н., нали? Но ако не сте Юлий Цезар, тогава обикновено работите с един раздел/инстанция наведнъж. Така че би било добре да имате възможност да поддържате една единствена връзка с уеб сокет или слушател на събития на сървъра за всичките ви екземпляри в браузъра.

За да постигнете това, можете да използвате споделен работник — специален тип уеб работник, който може да бъде достъпен от множество екземпляри на приложение. Споделените работници имат една нишка за изпълнение и споделен обхват между всички клиенти, което позволява ефективна комуникация и синхронизиране между раздели.

Тук е мястото, където Web API може да ни помогне да намалим броя на връзките, да увеличим производителността на сървъра и дори да намалим броя на допълнителните визуализации от страна на интерфейса.

Тази статия ще говори конкретно за Broadcast Channel API и ще опише някои практически случаи на използването му в приложения от реалния живот.

API за излъчване на канал

На първо място, като цяло Broadcast Channel API е JavaScript интерфейс, който позволява комуникация между различни контексти на сърфиране, като раздели или рамки (или между Node.js работни нишки, но за това в края на статията), които споделят един и същ произход . Той предоставя прост модел публикуване-абониране за изпращане и получаване на съобщения между тези контексти.

Почти всичко може да мине като съобщенията:

  • Всички примитивни типове с изключение на символи (Boolean, Null, Undefined, Number, BigInt, String)
  • Boolean и String обекти
  • Дати
  • Регулярни изрази
  • Петна
  • Файлове, FileLists
  • ArrayBuffers, ArrayBufferViews
  • ImageBitmaps, ImageDatas
  • Масиви, обекти, карти и множества

Прост пример за използване — приложения за табло. Да предположим, че имате приложение за табло, което показва данни в реално време от множество източници (като цени на акции, актуализации за времето, емисии с новини и т.н.). Можете да използвате BroadcastChannel за излъчване на актуализации от всеки източник на данни към всички отворени раздели на таблото.

Едно важно ограничение на Broadcast Channel API е, че работи само между контексти на сърфиране, които имат един и същ произход. Това означава, че не можете да го използвате за комуникация между различни уебсайтове или домейни, различни портове или схеми. Освен това има ограничения за размера и честотата на съобщенията, които могат да се изпращат по канала, за да се предотврати злоупотреба и да се гарантира производителност.

Страхотен и вече може да се използва. Например отворихте няколко раздела и искате да промените езика на приложението. След успешно превключване към друг език в един раздел конкретно съобщение (с тип „change_language“ например) ще бъде изпратено чрез Broadcast Channel API и получено от други раздели, които също могат да превключват езика.

Страхотно, с едно кликване променихме езика във всички раздели в браузъра. Но какво ще кажете за различните браузъри или дори устройства?

В този случай трябва да имаме бекенда на приложението.

Уебсокети

Нека имаме някаква крайна точка за промяна на езика. След обработване на заявката сървърът ще изпрати съобщение чрез websocket връзка.
Websockets е протокол, който позволява комуникация в реално време между клиент и сървър. Той позволява двупосочна комуникация с ниска латентност през една TCP връзка, което го прави идеален за приложения, които изискват пренос на данни в реално време.

Ето пример как да използвате Websockets:

Ако отворим тази връзка за всеки от разделите, какво ще получим?

Забележка. Разбира се, да имате собствен бекенд не е единствената възможност. Можем да използваме някои услуги като Firebase за съхранение в реално време например. Това е хоствана в облака NoSQL база данни, която позволява на разработчиците да съхраняват и синхронизират данни в реално време между множество клиенти. Той предоставя лесен за използване API за четене и запис на данни, както и вградена поддръжка за синхронизация в реално време и офлайн постоянство на данни.

Оптимизация

Всичко изглежда наред. Заявката за промяна на езика беше обработена успешно, имаше повторно изобразяване на текущия раздел (защото не искаме потребителите да чакат някои допълнителни съобщения от сървъра) след заявката. След това съобщение с тип „change_language“ беше изпратено през всички отворени уебсокет връзки към други устройства, браузъри и раздели. Повторно изобразяване се случи и там. Всичко изглежда наред? Да, като цяло, но имаме двойно повторно изобразяване за текущия раздел (което не е толкова важно) и броят на уебсокет връзките е равен на общия брой раздели във всички браузъри на всички устройства. Какво можем да направим с това?

В този случай можем да използваме Service Workers — тип уеб работник, който работи във фонов режим на уеб приложение, независимо от потребителския интерфейс. Те могат да прихващат и обработват мрежови заявки, да кешират данни за офлайн употреба и да изпращат известия до потребителя. Като цяло е много полезно, но супер полезно в нашия конкретен случай. Можем да отворим една единствена websocket връзка и да я използваме за изпращане на съобщения до всички раздели чрез Broadcast Channel API.

Между другото, можем да използваме същата архитектура по обратния начин и да използваме една връзка не само за получаване на нещо от сървъра, но и за изпращане.

Забележка. Да, вероятно ще бъде пречка в някои случаи. Ако връзката се прекъсне например, тя ще бъде загубена за всички раздели в приложението, но в същото време в някои случаи ще е по-добре да има постоянни грешки, отколкото частично налично приложение (изглежда като тема за допълнително проучване и статия, нали? ).

Така че, ако всичко е настроено правилно, имаме броя на връзките, равен на броя отворени браузъри, а не раздели. Не е ли страхотно?

Най-общо нашият случай изглежда така:

API, разбира се, може да изпраща заявки до множество Service Workers (множество браузъри) наведнъж. Той е ограничен от ресурсите на сървъра.

Последното малко нещо, което трябва да се подобри, е двойното изобразяване на текущия раздел. Това може да се направи чрез просто създаване на версии на нашите промени. Например ще добавим версия към нашия магазин (или друга архитектура за управление на състоянието на интерфейса). В нашия прост случай това може да бъде просто обикновен времеви печат (в повечето случаи непрекъснато нарастващ номер - точно това, от което се нуждаем).

Ще обработваме версията в следващите стъпки:

1. Добавете версия към тялото на заявката

2. Променете версията на магазина в състоянието на текущия раздел веднага след заявката

3. Добавяне на версия към websocket съобщение

4. Изпратете го чрез Broadcast Channel API

5. Проверете, преди да добавите към магазин/повторно изобразяване и игнорирайте промените, ако версията е равна (това условие ще бъде надеждно за текущия раздел) или дори по-висока от текущата

Забележка. Да, можем да проверим целия магазин за промени, преди да добавим/изобразим повторно, но това ще бъде допълнителна трудност — магазините могат да бъдат сравнително огромни, с вложени елементи, които трябва да използват някаква повтаряща се функция, ако използваме хешове за това, ще има проблем с реда на имотите и др.

Node.js бонус

Сега имаме възможност да работим с Broadcast Channel API не само в браузъра, но и от страната на сървъра.

Broadcast Channel е вграден API в Node.js, който позволява комуникация между нишки между работни нишки. Той позволява излъчване на съобщения между нишки по начин, подобен на API на Broadcast Channel на браузъра.

Ето пример за това как да използвате BroadcastChannel с Node.js работни нишки:

В този пример първо създаваме нов BroadcastChannel в основната нишка и стартираме две работни нишки с помощта на класа Worker.

След това изпращаме съобщение до двете работни нишки, използвайки метода postMessage() на канала.

В работните нишки създаваме нов BroadcastChannel и слушаме за входящи съобщения, използвайки свойството onmessage на канала. След това можем да обработим полученото съобщение и да изпратим съобщение обратно към основната нишка, използвайки метода postMessage() на обекта parentPort.

Заключение

Това е всичко засега.
Надявам се, че това ще ви помогне да създадете вашето перфектно приложение.
Ако харесвате статията, последвайте ме в Medium, Linkedin и Telegram.



Повече съдържание в PlainEnglish.io.

Регистрирайте се за нашия безплатен седмичен бюлетин. Следвайте ни в Twitter, LinkedIn, YouTube и Discord .