Real-Time Backend

Сравнение подходов

Как Slack выбирал протокол

В 2012 году команда Slack выбирала между polling и WebSocket. Выбор WebSocket потребовал sticky sessions на load balancer, Redis pub/sub между серверами и отдельного слоя WebSocket gateway. Polling был бы проще - но при 10 000 одновременных пользователей создал бы 600 000 запросов в минуту. WebSocket победил не потому что «современнее», а потому что chat - это двусторонняя связь с требованием к latency. Именно этот сценарий и оправдывает архитектурную сложность.

Матрица сравнения подходов

Четыре подхода к real-time коммуникации решают одну задачу разными компромиссами. Выбор между ними - это инженерное решение, а не вкусовщина: неверный выбор проявляется в счётах за сервер, жалобах на батарею и сложности горизонтального масштабирования.

ПараметрPollingLong PollingSSEWebSocket
Направлениеclient → serverclient → serverserver → clientдвустороннее
Latency~poll interval~100-500 мс~50-100 мс~1-50 мс
HTTP overheadвысокий (заголовки каждый запрос)среднийнизкий (одно соединение)минимальный (framing)
Proxy/firewallотличнохорошохорошопроблемы с некоторыми прокси
Mobile batteryплохосреднехорошохорошо
Auto-reconnectвстроенручнойвстроен (браузер)ручной
Browser support100%100%все кроме IEвсе кроме IE
Сложностьнизкаясредняянизкаявысокая

Числа для понимания масштаба: polling каждые 1 секунду на мобильном приложении - это 86 400 HTTP-запросов в день на одного клиента. При 10 000 активных пользователей: 864 000 000 запросов/день, каждый с полными HTTP-заголовками (~800 байт).

HTTP/2 меняет уравнение для SSE: multiplexing позволяет одному TCP-соединению нести несколько SSE-потоков. Polling при HTTP/2 дешевле, чем при HTTP/1.1, но SSE всё равно эффективнее при высокой частоте событий.

Приложение использует polling каждые 2 секунды. При 5 000 одновременных пользователях сколько запросов в минуту получает сервер?

Когда выбрать Polling

Polling выглядит как антипаттерн на фоне WebSocket и SSE - но это не так. Существуют сценарии, где polling является правильным архитектурным выбором: простота реализации, отсутствие stateful соединений и предсказуемая нагрузка перевешивают overhead.

  • **Данные меняются редко и предсказуемо** - курс валют обновляется раз в минуту, статус заказа меняется несколько раз в день
  • **Нет требований к latency** - аналитический dashboard, где задержка в 30 секунд не ощущается
  • **Legacy инфраструктура** - корпоративные прокси и файрволы, которые режут WebSocket и длинные соединения
  • **Простота важнее эффективности** - прототип, MVP, внутренний инструмент с 10 пользователями
  • **Idempotent операция** - клиент сам контролирует частоту и может адаптировать её к данным (exponential backoff)

**Реальный пример:** GitHub Actions использует polling в своём web UI. Статус workflow обновляется каждые несколько секунд через обычные HTTP-запросы - не WebSocket. Причина: простота кеширования на CDN, stateless сервера, совместимость с любым клиентом.

**ETag + 304 Not Modified** - способ снизить трафик при polling: сервер возвращает 304 без тела если данные не изменились. Клиент не тратит bandwidth на парсинг, сервер - на сериализацию.

Внутренний инструмент мониторинга показывает статус 5 серверов, данные обновляются раз в минуту, пользователей 3 человека. Какой подход рационален?

Когда выбрать SSE

Server-Sent Events оптимальны для задачи «сервер говорит, клиент слушает». Одностороннее соединение с автоматическим reconnect, нативный браузерный API, работа поверх обычного HTTP - всё это делает SSE элегантным выбором для стриминга данных.

  • **Live logs и progress** - стриминг вывода деплоя, прогресс бар обработки файла, потоковый ответ LLM
  • **Уведомления** - push-нотификации без необходимости клиентских отправок
  • **Обновления курсов/цен** - данные идут только с сервера, клиент только принимает
  • **Dashboard с частыми обновлениями** - метрики, графики в реальном времени

**Ограничение HTTP/1.1:** браузер разрешает максимум 6 одновременных соединений на домен. При HTTP/1.1 каждый SSE занимает одно соединение - при 6 открытых вкладках новые SSE не откроются. HTTP/2 решает проблему через multiplexing: одно TCP-соединение, много потоков.

**Last-Event-ID** - встроенный механизм resumability: браузер при переподключении отправляет заголовок `Last-Event-ID` с последним полученным id. Сервер может воспроизвести пропущенные события - надёжность без дополнительного протокола.

Нужно отображать потоковый вывод AI-генерации (только текст, сервер -> клиент, автоматическое переподключение при обрыве). Что выбрать?

Когда выбрать WebSocket

WebSocket открывает постоянный двусторонний канал после HTTP upgrade handshake. Это единственный вариант когда клиент и сервер должны обмениваться сообщениями с низкой latency в обоих направлениях.

  • **Chat и messaging** - клиент отправляет, сервер рассылает другим участникам
  • **Collaborative editing** - конфликты CRDT, operational transforms требуют двусторонней синхронизации
  • **Multiplayer игры** - позиции игроков, действия, состояние - высокая частота в обе стороны
  • **Trading терминалы** - latency < 50 мс критична, постоянный поток bid/ask + отправка ордеров
  • **Cursor sharing / whiteboard** - координаты мыши каждые 50 мс

**Стоимость WebSocket:** stateful соединения усложняют горизонтальное масштабирование. Если клиент подключён к серверу A, а сообщение для него пришло на сервер B - нужен механизм маршрутизации между узлами.

**Прокси и файрволы:** некоторые корпоративные прокси разрывают соединения дольше 60-120 секунд или не поддерживают Upgrade заголовок. Решение - WebSocket over TLS (wss://) на порту 443, прокси часто пропускает его как обычный HTTPS.

WebSocket всегда лучше SSE и Polling - он современнее и быстрее

WebSocket оптимален только при двусторонней связи с низкой latency. Для one-way стриминга SSE проще и надёжнее. Для редких обновлений polling имеет нулевую сложность масштабирования.

WebSocket вводит stateful соединения: нужны sticky sessions или pub/sub между серверами. Это реальная архитектурная сложность. SSE и Polling stateless - горизонтальное масштабирование сводится к добавлению инстансов за load balancer.

Система collaborative editing: два пользователя одновременно редактируют документ, изменения должны синхронизироваться за < 100 мс. Что выбрать?

Матрица решений

  • Polling: данные меняются редко, latency не критична, нужна простота - stateless, легко масштабируется
  • SSE: сервер стримит клиенту (логи, прогресс, нотификации) - автоматический reconnect, Last-Event-ID, HTTP/2 friendly
  • WebSocket: двусторонняя связь с latency < 100 мс (chat, collaborative, gaming) - stateful, требует pub/sub для масштабирования

Связанные темы

Выбор протокола влияет на всю архитектуру real-time системы.

  • WebSocket: протокол и handshake — Детали протокола из урока 1
  • SSE и Long Polling — Реализация SSE и Long Polling из уроков 2-3
  • Масштабирование real-time систем — Redis pub/sub и sticky sessions при WebSocket масштабировании

Вопросы для размышления

  • При каких условиях polling с ETag и 304 становится сопоставим по эффективности с SSE, и когда это различие перестаёт быть значимым?
  • Почему stateful соединения WebSocket усложняют горизонтальное масштабирование, и какие архитектурные паттерны решают эту проблему?
  • Как ограничение в 6 SSE-соединений на домен при HTTP/1.1 влияет на проектирование многовкладочных веб-приложений?

Связанные уроки

  • rt-03-sse — SSE is one of the approaches being compared; need to understand it before comparing
  • rt-05 — WebSocket protocol is the other main approach being contrasted
  • rt-07 — After choosing the right approach, the next step is building a first real-time app
  • rt-02-http-limits — Understanding HTTP limits motivates why multiple approaches exist at all
  • net-63-realtime-compare
Сравнение подходов

0

1

Войти