Сървърът изпрати събития и ограничения на браузъра

Имам уеб приложение, което следи за изпратени от сървъра събития. Докато работех и тествах с множество отворени прозорци, нещата не работеха и си блъсках главата няколко пъти, гледайки в грешната посока: в крайна сметка разбрах, че проблемът е в едновременните връзки.

Въпреки това тествах много ограничен брой и дори ако провеждам теста на Apache (знам, че трябва да използвам възел).

След това превключих браузъра и забелязах нещо наистина интересно: очевидно Chrome ограничава връзките на изпратените от сървъра събития до 4-5, докато Opera не го прави. Firefox от друга страна след 4-5 едновременни връзки отказва да заредикоято и да едруга страница.

Каква е причината за това? Ограничението важи ли само за SSE връзки от един и същ източник или ще бъде същото, ако трябва да ги тествам да ги отворя от различен домейн? Има ли някакъв шанс да използвам неправилно SSE и това всъщност да блокира браузърите или това е известно поведение? Има ли начин да го заобиколите?


person Sunyatasattva    schedule 03.09.2013    source източник
comment
в windows това се контролира от настройка на регистъра, която IE, chrome и firefox спазват и която ограничава всички връзки, не само SSE. Имах същия проблем с websockets... не можете да измислите това...   -  person dandavis    schedule 25.02.2014


Отговори (3)


Начинът, по който това работи във всички браузъри, е, че всеки домейн получава ограничено количество връзки и ограниченията са глобални за цялото ви приложение. Това означава, че ако имате една отворена връзка за комуникация в реално време, имате една по-малко за зареждане на изображения, css и други страници. Освен това не получавате нови връзки за нови раздели или прозорци, всички те трябва да споделят едно и също количество връзки. Това е много разочароващо, но има основателни причини за ограничаване на връзките. Преди няколко години това ограничение беше 2 във всички браузъри (въз основа на правилата в (http://www.ietf.org/rfc/rfc2616.txt) HTTP1.1 спецификация), но сега повечето браузъри използват 4-10 връзки като цяло. От друга страна, мобилните браузъри все още трябва да ограничават броя на връзките с цел пестене на батерията.

Налични са следните трикове:

  1. Използвайте повече имена на хостове. С възлагане на изх. www1.domain.com, www2.domain.com получавате нови връзки за всяко име на хост. Този трик работи във всички браузъри. Не забравяйте да промените домейна на бисквитката, за да включите целия домейн (domain.com, а не www.domain.com)
  2. Използвайте уеб сокети. Уеб сокетите не са ограничени от тези ограничения и което е по-важно, те не се конкурират с останалото съдържание на вашите уебсайтове.
  3. Използвайте повторно същата връзка, когато отваряте нови раздели/прозорци. Ако сте събрали цялата комуникационна логика в реално време към център за извикване на обект, можете да извикате този обект във всички отворени прозорци по следния начин:

    window.hub = window.opener ? window.opener.hub || new Hub()

  4. или използвайте флаш - не е най-добрият съвет в наши дни, но все пак може да е опция, ако уебсокетите не са опция.
  5. Не забравяйте да добавите няколко секунди време между всяка SSE заявка, за да позволите заявките на опашката да бъдат изчистени, преди да започнете нова. Също така добавете малко повече време за изчакване за всяка секунда, в която потребителят е неактивен, по този начин можете да концентрирате сървърните си ресурси върху онези потребители, които са активни. Добавете и произволен брой забавяния, за да избегнете проблема с гръмотевичното стадо

Друго нещо, което трябва да запомните, когато използвате многонишков и блокиращ език като Java или C#, рискувате да използвате ресурси във вашата дълга заявка за анкета, които са необходими за останалата част от вашето приложение. Например в C# всяка заявка заключва обекта Session, което означава, че цялото приложение не реагира по време на активна SSE заявка.

NodeJs е страхотен за тези неща по много причини, както вече разбрахте, и ако използвате NodeJS, бихте използвали socket.io или engine.io, които се грижат за всички тези проблеми вместо вас, като използват websockets, flashsockets и XHR-polling а също и защото не блокира и е с една нишка, което означава, че ще консумира много малко ресурси на сървъра, когато чака неща за изпращане. C# приложение консумира една нишка на чакаща заявка, която отнема поне 2 MB памет само за нишката.

person Christian Landgren    schedule 26.02.2014
comment
Страхотен отговор! Знаете ли защо Firefox отказваше да отвори какъвто и да е друг домейн? - person Sunyatasattva; 26.02.2014
comment
Благодаря ; за съжаление трикът с window.opener ще работи само за дъщерни прозорци, които създавам от моето приложение; не за всеки раздел, който потребителят отваря сам. Веднага щом потребителят отвори твърде много раздели, аз съм прецакан... Знаете ли дали има начин да проверим дали сме на път да изчерпим всички отворени връзки? - person phtrivier; 26.02.2014
comment
Опитайте да използвате (window.parent || window.opener), което ще се погрижи и за двата сценария. Вижте също метода postMessage за изпращане на кръстосани съобщения. Не мисля, че има някакъв начин да видите количеството връзки, но по-скоро трябва да използвате свойството за изчакване, за да можете да реагирате на сценарии, при които заявката за дълго запитване спира други заявки. xhr = нов XMLHttpRequest(); xhr.timeout = 5000; xhr.ontimeout=timeoutFired; - person Christian Landgren; 27.02.2014
comment
Sunyatasattva - да - ВСИЧКИ връзки са включени - дори нови страници. Което означава, че ако имате четири отворени раздела, всеки със собствена връзка за дълго запитване, вие сте използвали всичките си връзки и тъй като заявките за дълго запитване отиваха към същия домейн, глобалното ограничение за този домейн беше изчерпано. Най-добрият начин да предотвратите това е да използвате отделен домейн за връзката с дълго запитване (или да използвате повторно връзки), което означава, че обикновеното зареждане на страницата няма да бъде засегнато - само връзката в реално време. - person Christian Landgren; 27.02.2014
comment
Моля, маркирайте това като отговор, ако сте доволни? - person Christian Landgren; 27.02.2014
comment
@ChristianLandgren Поправете ме, ако греша, но не мисля, че два отделни раздела, отворени независимо в браузър, споделят window.parent? - person phtrivier; 27.02.2014

Един от начините да заобиколите този проблем е да изключите връзките на всички скрити раздели и да се свържете отново, когато потребителят посети скрит раздел.

Работя с приложение, което уникално идентифицира потребителите, което ми позволи да внедря това просто решение:

  1. Когато потребителите се свържат със sse, съхранявайте техния идентификатор, заедно с клеймо за времето, когато техният раздел е зареден. Ако в момента не идентифицирате потребители в приложението си, обмислете използването на сесии и бисквитки.
  2. Когато се отвори нов раздел и се свърже със sse, във вашия код от страна на сървъра изпратете съобщение до всички други връзки, свързани с този идентификатор (които нямат текущото времево клеймо), като казвате на предния край да затвори EventSource. Предният манипулатор би изглеждал по следния начин:

    myEventSourceObject.addEventListener('close', () => { myEventSourceObject.close(); myEventSourceObject = null; });

  3. Използвайте API за видимост на страницата в JavaScript, за да проверите дали стар раздел е видим отново и свържете отново този раздел към SS, ако е така.

    document.addEventListener('visibilitychange', () => { if (!document.hidden && myEventSourceObject === null) { // reconnect your eventsource here } });

  4. Ако настроите кода на вашия сървър, както е описано в стъпка 2, при повторно свързване кодът от страната на сървъра ще премахне всички други връзки към sse. Следователно можете да кликвате между вашите раздели и EventSource за всеки раздел ще бъде свързан само когато разглеждате страницата.

Имайте предвид, че API за видимост на страницата не е наличен в някои стари браузъри: https://caniuse.com/#feat=pagevisibility

person NerdSoup    schedule 09.06.2019
comment
Имайте предвид, че API за видимост на страницата използва префикси на браузъра за някои по-стари браузъри. Вижте този малък пакет на npm, ако не искате да ги внедрите ръчно: npmjs.com/package/ visibilityjs - person NerdSoup; 10.06.2019
comment
Лошо предложение. Не можете да спрете връзката, защото потребителят ще спре да получава известия в реално време. ако потребителят се свърже отново, трябва да актуализира страницата, за да получи новините. - person Freddy Daniel; 27.10.2020

Прав си за броя на едновременните връзки.

Можете да проверите този списък за максимални стойности: http://www.browserscope.org/?category=network

И за съжаление, никога не намерих никаква работа, освен мултиплексиране и/или използване на различни имена на хостове.

person avetisk    schedule 25.02.2014