Подтвердить что ты не робот

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

У меня есть веб-приложение, которое прослушивает события, отправленные сервером. Пока я работал и тестировался с несколькими открытыми окнами, все не работало, и я несколько раз бил головой, глядя в неправильном направлении: в конце концов, я понял, что проблема связана с одновременными подключениями.

Однако я тестировал очень ограниченное число и даже если я запускаю тест на Apache (я знаю, я должен использовать node).

Затем я переключил браузер и заметил что-то действительно интересное: по-видимому, Chrome ограничивает соединения с сервером Sent Events с 4-5, а Opera - нет. Firefox, с другой стороны, после 4-5 одновременных подключений отказывается загружать любую другую страницу.

В чем причина этого? Лимит применяется только к соединениям SSE из того же источника, или это будет то же самое, если я должен проверить, открывать их из другого домена? Есть ли вероятность, что я злоупотребляю SSE, и это фактически блокирует браузеры, или это известное поведение? Есть ли способ обойти это?

4b9b3361

Ответ 1

Способ, которым это работает во всех браузерах, заключается в том, что каждый домен получает ограниченное количество соединений, а ограничения являются глобальными для всего вашего приложения. Это означает, что если у вас есть одно соединение для обмена в реальном времени, у вас есть меньше всего для загрузки изображений, css и других страниц. Кроме того, вы не получаете новых подключений для новых вкладок или окон, все они должны иметь одинаковое количество соединений. Это очень расстраивает, но есть веские причины для ограничения соединений. Несколько лет назад этот предел составлял 2 во всех браузерах (в соответствии с правилами в (http://www.ietf.org/rfc/rfc2616.txt) спецификацией HTTP1.1), но теперь большинство браузеров используют 4-10 соединений вообще. Для мобильных браузеров, с другой стороны, по-прежнему необходимо ограничить количество подключений для экономии заряда аккумулятора.

Эти трюки доступны:

  • Используйте больше имен хостов. Назначая ex. www1.domain.com, www2.domain.com, вы получаете новые подключения для каждого имени хоста. Этот трюк работает во всех браузерах. Не забудьте изменить домен cookie, чтобы включить весь домен (domain.com, а не www.domain.com).
  • Используйте веб-сокеты. Веб-сокеты не ограничены этими ограничениями, и, что более важно, они не конкурируют с остальными вашими веб-сайтами.
  • Повторное использование такого же соединения при открытии новых вкладок/окон. Если вы собрали всю коммуникационную логику реального времени в концентратор объектов, вы можете вызвать этот объект во всех открытых окнах, как это:

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

  • или использовать flash - не самый лучший совет в наши дни, но он все равно может быть вариантом, если веб-узлы не являются опцией.
  • Не забудьте добавить несколько секунд времени между каждым запросом SSE, чтобы разрешить очистку запросов в очереди, прежде чем запускать новый. Также добавьте немного больше времени ожидания для каждой секунды, когда пользователь неактивен, таким образом вы можете сконцентрировать ресурсы сервера на активных пользователях. Также добавьте случайное число задержек, чтобы избежать проблемы Громового стада

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

NodeJs отлично подходит для этих вещей по многим причинам, как вы уже выяснили, и если вы используете NodeJS, вы бы использовали socket.io или engine.io, который заботится обо всех этих проблемах для вас, используя веб-порты, флеш-память и XHR-опрос, а также потому, что он не является блокирующим и однопоточным, что означает, что он будет потреблять очень мало ресурсов на сервере, когда он ждет отправки вещей. Приложение С# потребляет один поток на запрос ожидания, который занимает не менее 2 МБ памяти только для потока.

Ответ 2

Вы правы в отношении количества одновременных подключений.

Вы можете проверить этот список на максимальные значения: http://www.browserscope.org/?category=network

И, к сожалению, я никогда не работал, кроме мультиплексирования и/или использования разных имен хостов.

Ответ 3

Одним из способов решения этой проблемы является отключение соединений на всех скрытых вкладках и повторное подключение, когда пользователь заходит на скрытую вкладку.

Я работаю с приложением, которое однозначно идентифицирует пользователей, что позволило мне реализовать этот простой обходной путь:

  1. Когда пользователи подключаются к sse, сохраняют их идентификатор вместе с отметкой времени, когда их вкладка загружается. Если вы в настоящее время не идентифицируете пользователей в своем приложении, рассмотрите возможность использования сеансов и файлов cookie.
  2. Когда открывается новая вкладка и подключается к sse, в своем коде на стороне сервера отправьте сообщение всем другим подключениям, связанным с этим идентификатором (которые не имеют текущей метки времени), сообщая внешнему интерфейсу закрыть EventSource. Внешний обработчик будет выглядеть примерно так:

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

  3. Используйте API видимости страницы javascript, чтобы проверить, видна ли старая вкладка снова, и повторно подключите эту вкладку к sse, если она есть.

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

  4. Если вы настроили свой серверный код, как описано в шаге 2, при повторном подключении серверный код удалит все остальные подключения к sse. Следовательно, вы можете щелкнуть между своими вкладками, и EventSource для каждой вкладки будет подключен только при просмотре страницы.

Обратите внимание, что API видимости страницы недоступен в некоторых старых браузерах: https://caniuse.com/#feat=pagevisibility