Перейти к содержанию
Innovialabs

WebSockets, SSE или polling: что выбрать для real-time

Продвинутое4 мин

Коротко

Для двустороннего обмена (чаты, совместное редактирование, игры) берите WebSockets; для потока событий только от сервера (уведомления, дашборды, стриминг ответа AI) — SSE; polling оставьте как простой запасной вариант.

Короткий ответ

ЗадачаЛучший выбор
Чат, совместное редактирование, мультиплеерWebSockets
Уведомления, лента событий, live-дашбордSSE
Стриминг ответа LLM по токенамSSE (или стриминг HTTP-ответа)
Редкие обновления, статус фоновой задачиPolling
Окружение, где ничего кроме обычного HTTP не проходитLong polling

Главный вопрос — кто кому пишет. Если данные идут только от сервера к клиенту, WebSockets обычно избыточны.

Как работает каждый механизм

Short polling. Клиент раз в N секунд спрашивает: «есть новое?». Просто и надёжно, но задержка равна интервалу, а большинство запросов возвращаются пустыми.

Long polling. Клиент отправляет запрос, сервер держит его открытым, пока не появятся данные или не истечёт таймаут. После ответа клиент сразу отправляет новый запрос. Задержка ниже, но каждое сообщение — это новый HTTP-цикл с заголовками.

Server-Sent Events (SSE). Клиент открывает обычный HTTP-запрос, сервер отвечает с Content-Type: text/event-stream и не закрывает соединение, отправляя события текстом. Направление — только сервер → клиент. В браузере есть встроенный EventSource с автоматическим переподключением.

const es = new EventSource("/api/events");
es.onmessage = (e) => console.log(JSON.parse(e.data));

WebSockets. Соединение начинается как HTTP и переключается (Upgrade) на отдельный протокол. Дальше обе стороны отправляют сообщения в любой момент, текстовые или бинарные, с минимальными накладными расходами.

const ws = new WebSocket("wss://example.com/chat");
ws.onmessage = (e) => render(JSON.parse(e.data));
ws.send(JSON.stringify({ text: "Привет" }));

Сравнение

PollingLong pollingSSEWebSockets
НаправлениеКлиент → серверКлиент → серверСервер → клиентОба
ЗадержкаИнтервал опросаНизкаяНизкаяНизкая
ПротоколHTTPHTTPHTTPОтдельный, после Upgrade
ПереподключениеНе нужноВручнуюВстроено в EventSourceВручную
Бинарные данныеДаДаНет, только текстДа
Совместимость с проксиОтличнаяХорошаяХорошая, если отключена буферизацияНужна настройка

Прокси, балансировщики и инфраструктура

Здесь чаще всего и ломается real-time.

  • Буферизация. nginx и другие прокси могут буферизовать ответ, и события SSE придут пачкой. Для SSE отключайте буферизацию (в nginx — proxy_buffering off или заголовок X-Accel-Buffering: no).
  • Таймауты простоя. Прокси и балансировщики закрывают «тихие» соединения. Отправляйте heartbeat: комментарий : в SSE или ping/pong в WebSockets, и увеличивайте proxy_read_timeout.
  • Upgrade для WebSockets. Прокси должен пробрасывать заголовки Upgrade и Connection, иначе рукопожатие не пройдёт.
  • HTTP/1.1 и лимит соединений. Браузер ограничивает число одновременных соединений к одному домену по HTTP/1.1, и несколько вкладок с SSE могут его исчерпать. По HTTP/2 потоки мультиплексируются, и проблема уходит.
  • Serverless. Многие serverless-платформы ограничивают длительность запроса и плохо подходят для долгоживущих WebSocket-соединений. SSE с ограниченной длительностью работает чаще.

Масштабирование

Долгоживущее соединение привязано к конкретному серверу. Когда серверов несколько:

  1. Общая шина сообщений. Событие, возникшее на сервере A, должно дойти до клиента, подключённого к серверу B. Используют Redis Pub/Sub, NATS, Kafka или управляемые сервисы.
  2. Sticky sessions. Нужны для long polling и некоторых библиотек с fallback-механизмом, чтобы запросы клиента попадали на тот же сервер.
  3. Лимиты ресурсов. Каждое соединение занимает память и файловый дескриптор. Проверьте лимиты ОС и выбирайте асинхронный сервер.
  4. Плавный деплой. При перезапуске все клиенты переподключатся одновременно. Добавьте случайную задержку (jitter) в переподключение.

Переподключение и потеря сообщений

Соединения рвутся: смена сети, сон ноутбука, деплой. Планируйте это заранее.

  • SSE: отправляйте поле id: у событий. При переподключении браузер пришлёт заголовок Last-Event-ID, и сервер сможет дослать пропущенное.
  • WebSockets: переподключение пишется вручную — с экспоненциальной задержкой и jitter. После переподключения клиент запрашивает изменения с последнего известного момента.
  • Источник правды — сервер. Real-time-канал лучше считать способом доставки уведомлений, а актуальное состояние получать отдельным запросом.

Частые ошибки

  • WebSockets там, где хватило бы SSE, а с ними — лишняя инфраструктура.
  • Нет heartbeat, и соединения тихо умирают через минуту на прокси.
  • Нет авторизации при установке соединения или проверка токена только один раз на часы работы.
  • Масштабирование на несколько серверов без общей шины: часть клиентов не получает события.

Частые вопросы

Ответ модели идёт в одну сторону — от сервера к клиенту, а запрос пользователя отправляется обычным HTTP. SSE работает поверх HTTP, проще проходит через прокси и не требует отдельного протокола.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.