Logo GH

WebSockets и SSE

1) Коротко: что и для чего

WebSocket (WS/WSS) — апгрейд HTTP-соединения до полно-дуплексного канала. Подходит для чатов, live-игр, коллаборации, двунаправленной телеметрии.
Server-Sent Events (SSE) — односторонний стрим от сервера к браузеру (MIME `text/event-stream`). Идеален для тикеров, уведомлений, котировок, прогресса задач. Клиент — `EventSource`.

Правило выбора:
  • Нужен ввод от клиента в режиме реального времени (часто и много) → WebSocket.
  • Только пуш-обновления с сервера, совместимость и простота важнее → SSE.

2) Сеть и протоколы

2.1 Транспорт и совместимость

WebSocket: стартует как HTTP `GET … Upgrade: websocket` (HTTP/1.1). Для HTTP/2 возможен RFC 8441 (CONNECT + `:protocol = websocket`), поддержка зависит от прокси. Работает поверх TLS (WSS) — обязательно в проде.
SSE: обычный длительный HTTP-ответ (`200 OK`) c потоковой передачей. Прекрасно идет через HTTP/1.1/2/3, совместим с CDN/прокси (если не обрывают длинные коннекты).

2.2 Прокси/балансировщики/CDN

Проверьте: поддержка долгоживущих соединений, идла и таймаутов, sticky-сессий (если состояние на узле).
Для WS: включите `proxy_read_timeout`, `upgrade`-заголовки, пер-соединение лимиты.
Для SSE: убедитесь, что прокси не буферизует ответ (иначе клиент не увидит события вовремя).

3) Модель сообщений и управление потоком

WebSocket: кадры текст или бинарные; есть `ping/pong`, но встроенного backpressure нет — реализуйте на приложении (очереди, окна, drop-policy).
SSE: текстовые события (UTF-8); клиент встроенно умеет reconnect с задержкой; сервер может задавать `retry:`. Есть `id:` и заголовок `Last-Event-ID` для возобновления с нужной позиции.

Backpressure (общие практики):
  • Квотируйте исходящие сообщения per-клиент.
  • Ограничивайте очередь неприсланных событий; при переполнении — отбрасывать низкоприоритетные/агрегировать.
  • Для WS: используйте «скользящее окно» и ACK-схему на уровне приложения.

4) Надежность соединений

4.1 Детекция и keepalive

WS: отправляйте `ping` каждые N секунд; разрыв по таймауту — reconnect с экспоненциальным backoff + джиттер.
SSE: сервер шлет «комментарии» `:\n` как heartbeat, чтобы соединение не считали idle; клиент сам переподключится.

4.2 Восстановление потока

WS: держите offset/sequence сообщений и запрашивайте дельту после reconnect.
SSE: используйте `id:` для каждого события и `Last-Event-ID` в запросе — сервер шлет пропущенные события.

5) Аутентификация и авторизация

JWT Bearer в URL запроса (WS) небезопасен (утечки в логах). Используйте заголовок (через первичный HTTP-хэндшейк) или куки с флагами `Secure`, `HttpOnly`, `SameSite`.
Возможен mTLS (особенно для B2B), а также подписи (HMAC) поверх первоначального запроса.
Для SSE с куками помните про CORS (`Access-Control-Allow-Origin`, `Allow-Credentials`).

Ротация токена: не обрывайте стрим. Передавайте «скоро истечет» → клиент переоткроет соединение с новым токеном.

6) Формат данных и компрессия

WebSocket: включайте permessage-deflate осторожно (CPU); избегайте компрессии уже-сжатых форматов (Proto, Avro). Бинарные payload экономнее JSON.
SSE: это текст; для крупных данных отправляйте ссылку на REST/gRPC ресурс или chunk-файлы; транспортный gzip для SSE — уместен, но следите за буферизацией в прокси/CDN.

7) Масштабирование и фан-аут

7.1 Горизонтальное масштабирование

Держите приложение бездефектным к перезапускам. Состояние соединений — во фронт-слое; данные — из брокера.
Sticky (хэш по session/user) нужен, если есть локальные очереди.
Идеал — stateless фронт: узел лишь мультиплексирует подписки; события приходят из общего pub/sub.

7.2 Pub/Sub и брокеры

Для широкого фан-аута используйте Kafka/NATS/Redis Streams.
Слой «Fanout Gateway» подписывается на топики и пушит клиентам по WS/SSE.
Применяйте ключ маршрутизации (например, `userId`, `matchId`) для балансировки нагрузки между нодами.

8) Производственные конфиги

8.1 NGINX — WebSocket

nginx map $http_upgrade $connection_upgrade { default upgrade; '' close; }

server {
listen 443 ssl http2;
server_name ws. example. com;

location /ws {
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_http_version 1. 1;
proxy_read_timeout 75s; # increase for long sessions proxy_send_timeout 15s;
proxy_pass http://ws-backend;
}
}

8.2 NGINX — SSE (важно отключить буферизацию)

nginx location /events {
proxy_http_version 1. 1;
proxy_set_header Connection "";
proxy_buffering off; # is critical for proxy_cache off threads;
chunked_transfer_encoding on;
proxy_read_timeout 60m;
proxy_pass http://sse-backend;
}

8.3 Kubernetes (Ingress Annotations, NGINX Ingress)

yaml metadata:
annotations:
nginx. ingress. kubernetes. io/proxy-read-timeout: "3600"
nginx. ingress. kubernetes. io/proxy-send-timeout: "3600"
nginx. ingress. kubernetes. io/enable-websocket: "true"
nginx. ingress. kubernetes. io/proxy-buffering: "off"  # для SSE

9) Примеры кода

9.1 Клиент SSE (браузер)

js const es = new EventSource("/events? channel=odds", { withCredentials: true });

es. addEventListener("message", (e) => {
const data = JSON. parse(e. data);
renderOdds(data);
});

es. addEventListener("error", () => {
//EventSource will reconnect itself; can be shown spinner
});

9.2 Сервер SSE (Node.js/Express)

js app. get('/events', (req, res) => {
res. writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
res. write ('retry: 3000\n\n') ;//3s backoff to client

const sub = subscribe(req. query. channel, (event) => {
res. write(`id: ${event. id}\n`);
res. write(`event: message\n`);
res. write(`data: ${JSON. stringify(event. payload)}\n\n`);
});

req. on('close', () => sub. unsubscribe());
});

9.3 Клиент WebSocket (браузер)

js const ws = new WebSocket("wss://ws. example. com/ws");
ws. onopen = () => ws. send(JSON. stringify({ type: "join", room: "chat-1" }));
ws. onmessage = (m) => handle(JSON. parse(m. data));
ws. onclose = () => scheduleReconnect();

10) Безопасность и лимитирование

Rate limiting per-соединение и per-пользователь: ограничивайте частоту входящих сообщений (WS) и скорость исходящего потока (WS/SSE).
Quota по времени жизни соединения и суммарному трафику.
Message size limit и max messages/sec.
WAF/бот-фильтры на этапе хэндшейка; защита от connection flooding (много коротких коннектов).
Изоляция по tenant/namespace: отдельные пулы ресурсов.

11) Наблюдаемость

Метрики:
  • `connections_active`, `connections_new_total`, `bytes_in/out`,
  • `messages_in/out_total`, `dropped_messages_total`,
  • `reconnects_total`, `latency_delivery_ms{p50,p95,p99}`.
  • Логи: IP/UA, userId/tenantId, причина закрытия (`close_code`), длительность.
  • Трейсинг: связывайте события с исходной командой (корреляционные ID); для WS используйте «виртуальные» спаны по батчам.

12) Эксплуатационные нюансы

TLS-терминация ближе к клиенту (CDN/edge).
Проактивная ротация соединений (graceful) при деплоях: отдайте флаг «переподключись».
Шардирование по ключу, чтобы равномерно распределять «горячие» каналы.
Снимки состояния для поздних подписчиков (snapshot + delta).
Кэш последнего значения (SSE особенно) — полезно для «холодного» клиента.

13) Анти-паттерны

Стрим больших бинарных кусков через SSE или JSON по WS — используйте загрузку по HTTP и ссылку в сообщении.
Авторизация только в момент подключения и отсутствие пере-проверок на долгих сессиях.
Глобальный sticky без нужды → дисбаланс и «горячие» ноды.
Отключенные таймауты/лимиты → зависшие коннекты съедают пул.
Буферизация SSE прокси/CDN → «реальное время» превращается в минуты задержки.
Отсутствие sequence/offset → после reconnect клиент теряет целостность данных.

14) Чек-лист внедрения

  • Выбрана модель: WS (двунаправленно) или SSE (односторонне).
  • Настроены таймауты, keepalive и reconnect (backoff+джиттер).
  • Спроектированы sequence/offset и (для SSE) `id`/`Last-Event-ID`.
  • Определены лимиты: размер/скорость/соединения/квоты, защита от DoS.
  • Конфиг прокси/ингресса: upgrade, proxy_buffering off (SSE), read/send timeouts.
  • Масштабирование: pub/sub-брокер, фан-аут-шлюз, sticky только при необходимости.
  • Аутентификация: безопасная передача токена, ротация без обрыва.
  • Наблюдаемость: метрики, логи, трассы; дашборды и алерты.
  • План релизов: graceful-drain соединений, сигнал клиенту о пере-подключении.
  • Game Days: обрывы сети, падение ноды, перегруз брокера, длинные RTT.

15) FAQ

Можно ли кэшировать SSE через CDN?
Обычно нет: это персонализированный поток. Для публичных каналов — возможно с коротким TTL и chunked-delivery, но легко наломать «реальное время».

Работает ли WebSocket поверх HTTP/2/3?
Браузерный WS стартует с HTTP/1.1-upgrade; есть RFC 8441 для h2, поддержка в прокси/сервере требуется отдельно. С h3 — в движении; для стриминга в h3 есть WebTransport, но это другой API.

gRPC vs WS для браузера?
Чистый браузер не говорит gRPC; нужен gRPC-Web через Envoy. Для интерактивных UI часто проще WS + REST.

16) Итоги

WebSocket — когда нужен диалог в реальном времени и компактный двунаправленный канал.
SSE — когда нужен простой и надежный push от сервера к клиенту, минимальная сложность и автоматический reconnect.
Успех в проде — это правильные таймауты и лимиты, восстановление с offset, pub/sub-фан-аут, корректные настройки прокси и четкая наблюдаемость.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Telegram
@Gamble_GC
Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

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