WebSockets и SSE
1) Scurt: ce și pentru ce
WebSocket (WS/WSS) - upgrade de conexiune HTTP la full duplex canal. Potrivit pentru chat-uri, jocuri live, colaborare, telemetrie bidirecțională.
Evenimente trimise de server (SSE) - flux unic de la server la browser (MIME „text/eveniment-stream”). Ideal pentru tickers, notificări, citate, progresul sarcinii. Client - 'EventSource'.
- Aveți nevoie de intrare de la client în timp real (de multe ori o mulțime) → WebSocket.
- Numai actualizările push de pe server, compatibilitatea și simplitatea sunt mai importante decât → SSE.
2) Rețea și protocoale
2. 1 Transport și compatibilitate
WebSocket: Începe ca HTTP "GET... Actualizare: websocket '(HTTP/1. 1). Pentru HTTP/2, RFC 8441 (CONNECT + ': protocol = websocket') este posibil, suportul depinde de proxy. Lucrări pe partea de sus a TLS (WSS) - obligatorii în vânzări.
SSE: răspuns normal HTTP lung („200 OK”) cu streaming. Merge bine prin HTTP/1. 1/2/3, compatibil cu CDN/proxy (dacă conexiunile lungi nu sunt terminate).
2. 2 Proxies/balansoare/CDN
Verificați: suport pentru conexiuni de lungă durată, idles și timeout, sesiuni lipicioase (în cazul în care starea este pe nod).
Pentru WS: includeți anteturile „proxy _ read _ timeout”, „upgrade”, limitele per-connection.
Pentru SSE, asigurați-vă că proxy-ul nu tamponează răspunsul (altfel clientul nu va vedea evenimentele la timp).
3) Modelul mesajului și controlul debitului
WebSocket: cadre de text sau binare; există un 'ping/pong', dar nu există o backpressure încorporată - implementare pe aplicație (cozi, ferestre, politică drop-policy).
SSE: evenimente de text (UTF-8); clientul este capabil să se reconecteze cu o întârziere încorporată; serverul poate specifica 'retry:'. Există un 'id:' și un antet 'Last-Event-ID' pentru a relua din poziția dorită.
- Citați mesajele de ieșire pe client.
- Limitați coada evenimentelor necompletate; la depășire - aruncați prioritatea/agregatul scăzut.
- Pentru WS, utilizați o fereastră glisantă și un nivel de aplicare ACK.
4) Fiabilitatea conexiunilor
4. 1 Detectare și păstrare
WS: trimite „ping” la fiecare N secunde; decalaj de timp - reconectați-vă cu backoff exponențial + jitter.
SSE: serverul trimite "comentarii" ":\n' ca bătăi ale inimii, astfel încât conexiunea să nu conteze inactiv; clientul se va reconecta.
4. 2 Recuperarea fluxului
WS: păstrați mesajele offset/secvență și solicitați delta după reconectare.
SSE: utilizați „id:” pentru fiecare eveniment și „Last-Event-ID” în cerere - serverul trimite evenimente pierdute.
5) Autentificare și autorizare
Purtătorul JWT în URL-ul de cerere (WS) este nesigur (scurgeri în jurnale). Utilizați un antet (prin strângerea primară de mână HTTP) sau cookie-uri cu steagurile „Secure”, „HttpOnly”, „SameSite”.
mTLS (în special pentru B2B) este posibil, precum și semnarea (HMAC) peste cererea inițială.
Pentru SSE cu cookie-uri, amintiți-vă despre CORS („Access-Control-Allow-Origin”, „Permiteți-acreditări”).
Rotația tokenului: Nu tăiați fluxul. Pass' va expira în curând "→ clientul va redeschide conexiunea cu noul token.
6) Formatul și compresia datelor
WebSocket: activați permessage-dezumflați cu atenție (CPU); evitați comprimarea formatelor deja comprimate (Proto, Avro). Sarcina utilă binară este mai economică decât JSON.
SSE: acesta este textul; pentru date mari, trimiteți un link către o resursă REST/gRPC sau fișiere bucăți; transport gzip pentru SSE - adecvat, dar ceas pentru tamponare în proxy/CDN.
7) Scalare și fan-out
7. 1 Scalare orizontală
Păstrați aplicația fără defecte pentru reporniri. Starea conexiunii - în stratul frontal; date - de la broker.
Sticky (hash de sesiune/utilizator) este necesar dacă există cozi locale.
Ideal este partea din față apatrid: nodul doar multiplexuri abonamente; evenimentele provin dintr-un pub/sub comun.
7. 2 Pub/Sub și brokeri
Pentru un fan-out larg, utilizați Kafka/NATS/Redis Streams.
Stratul „Fanout Gateway” se abonează la subiecte și clienți fluffs prin WS/SSE.
Utilizați o cheie de rutare (de exemplu, „userId',” matchId') pentru a echilibra sarcina între noduri.
8) Configurații de producție
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 (important pentru a dezactiva tamponarea)
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 (Adnotări de intrare, 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) Exemple de cod
9. 1 client SSE (browser)
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 Server 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 client WebSocket (browser)
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) Securitate și limitare
Limitarea ratei per-conexiune și per-utilizator: limitați rata mesajelor primite (WS) și rata fluxului de ieșire (WS/SSE).
Cota prin durata de viață a conexiunii și traficul total.
Limita dimensiunii mesajului и mesaje max/sec.
Filtre WAF/bot la etapa strângerii de mână; protecție împotriva inundațiilor conexiunilor (multe conexiuni scurte).
Izolarea chiriașilor/namespace: piscine individuale de resurse.
11) Observabilitate
Măsurători:- 'conexiuni _ active', 'conexiuni _ noi _ totale', 'bytes _ in/out',
- 'messages _ in/out _ total', 'droped _ messages _ total',
- 'reconnects _ total', 'latency _ delivery _ ms {p50, p95, p99}'.
- Jurnale: IP/UA, userId/tenantId, motiv pentru închidere ('close _ code'), durată.
- Urmărire: asociați evenimentele cu comanda originală (ID-uri de corelare); pentru WS, utilizați deschideri butch „virtuale”.
12) Nuanțe operaționale
Terminarea TLS mai aproape de client (CDN/edge).
Rotirea proactivă a conexiunilor (grațios) în timpul epuizărilor: da pavilionul „reconecta”.
Sharding pe cheia pentru a distribui uniform canalele „fierbinte”.
Instantanee de stare pentru abonații târzii (instantaneu + delta).
Ultima valoare cache (SSE special) - util pentru un client rece.
13) Anti-modele
Redați bucăți binare mari prin SSE sau JSON prin WS - utilizați descărcarea HTTP și link-ul din mesaj.
Autorizarea numai în momentul conectării și absența re-verificărilor pentru sesiuni lungi.
Global lipicios inutil → dezechilibru și noduri fierbinți.
Temporizări/limite pentru persoanele cu handicap → conexiunile congelate consumă piscina.
SSE proxy/CDN tamponarea → „timp real” devine minute de întârziere.
Lipsa secvenței/decalajului → după reconectare, clientul își pierde integritatea datelor.
14) Lista de verificare a implementării
- WS (bidirecțional) sau SSE (unilateral) este selectat.
- Timeout configurat, keepalive și reconectați (backoff + jitter).
- Proiectat secvență/offset și (pentru SSE) 'id'/' Last-Event-ID'.
- Dimensiune/viteză/conexiuni/cote, DoS de protecție definite.
- Configurare proxy/intrare: upgrade, proxy_buffering off (SSE), citire/trimitere timeout.
- Scalare: pub/sub broker, fan-out gateway, lipicios numai dacă este necesar.
- Autentificare: transfer de token securizat, rotație fără întreruperi.
- Observabilitate: valori, busteni, urme; tablouri de bord și alerte.
- Planul de eliberare: conexiuni grațioase de scurgere, semnalizați clientului să se reconecteze.
- Zilele jocului: pauze de rețea, picături de nod, supraîncărcare broker, RTT-uri lungi.
15) ÎNTREBĂRI FRECVENTE
Poate fi SSE cache prin CDN?
De obicei nu: Este un flux personalizat. Pentru canale publice - eventual, cu scurt TTL și chunked-livrare, dar este ușor de a rupe „timp real”.
Funcționează WebSocket pe partea de sus a HTTP/2/3?
Browser-ul WS începe cu HTTP/1. 1-upgrade; există RFC 8441 pentru h2, suportul în proxy/server este necesar separat. Cu h3 - în mișcare; pentru streaming, h3 are WebTransport, dar este un API diferit.
gRPC vs WS pentru browser?
Un browser curat nu spune gRPC; aveți nevoie de gRPC-Web prin intermediul trimisului. Pentru interfețele interactive, WS + REST este adesea mai ușor.
16) Totaluri
WebSocket - atunci când aveți nevoie de dialog în timp real și un canal bidirecțional compact.
SSE - atunci când aveți nevoie de un impuls simplu și fiabil de la server la client, complexitate minimă și reconectare automată.
Succesul în vânzări este timpul și limitele corecte, recuperarea de la offset, pub/sub fan-out, setările corecte proxy și observabilitatea clară.