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') з потоковою передачею. Чудово йде через 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).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.