WebSockets и SSE
1) Bref : Quoi et à quoi sert
WebSocket (WS/WSS) est une mise à niveau de la connexion HTTP vers un canal duplex complet. Convient pour les chats, les jeux en direct, la collaboration, la télémétrie bidirectionnelle.
Server-Sent Event (SSE) : Efface à sens unique du serveur au navigateur (MIME 'text/event-stream'). Idéal pour les tickers, notifications, devis, progrès des tâches. Le client est 'EventSource'.
- Vous avez besoin d'une entrée en temps réel du client (souvent et beaucoup) → WebSocket.
- Seules les mises à jour push du serveur, la compatibilité et la simplicité sont plus importantes que les → SSE.
2) Réseau et protocoles
2. 1 Transport et interopérabilité
WebSocket : commence comme HTTP 'GET... Upgrade: websocket` (HTTP/1. 1). Pour HTTP/2 est possible RFC 8441 (CONNECT + ':protocol = websocket '), le soutien dépend de proxy. Fonctionne sur TLS (WSS) - obligatoire dans la vente.
SSE : réponse HTTP longue durée classique (« 200 OK ») avec streaming. Ça passe très bien par HTTP/1. 1/2/3, compatible avec CDN/proxy (sauf si les connexions longues sont coupées).
2. 2 Mandataires/équilibreurs/CDN
Vérifiez : prise en charge des connexions à longue durée de vie, de l'idle et des temporisateurs, des sessions de sticky (si l'état est sur le nœud).
Pour WS : incluez 'proxy _ read _ timeout', 'upgrade' --zagoches, limites de connexion per.
Pour SSE : assurez-vous que le proxy ne tamponne pas la réponse (sinon le client ne verra pas l'événement à temps).
3) Modèle de message et contrôle de flux
WebSocket : cadres texte ou binaires ; il y a 'ping/pong', mais il n'y a pas de backpressure intégrée - implémentez-le sur l'application (files d'attente, fenêtres, drop-policy).
SSE : événements textuels (UTF-8) ; le client est en mesure de reconnaître avec retard ; le serveur peut spécifier 'retry :'. Il y a 'id :' et le titre 'Last-Event-ID' pour reprendre à partir de la position souhaitée.
- Quantifiez les messages per-client sortants.
- Limiter la file d'attente d'événements non acceptés ; en cas de débordement, jeter les petites priorités/agréger.
- Pour WS : utilisez la « fenêtre glissante » et le schéma ACK au niveau de l'application.
4) Fiabilité des connexions
4. 1 Détection et keepalive
WS : envoyer 'ping' toutes les N secondes ; l'écart temporel est reconnect avec un backoff exponentiel + jitter.
SSE : serveur shlet « commentaires » ':\n'comme heartbeat afin que la connexion ne compte pas idle ; le client se reconnectera lui-même.
4. 2 Récupération de flux
WS : gardez les messages offset/sequence et demandez delta après reconnect.
SSE : utilisez 'id :' pour chaque événement et 'Last-Event-ID' dans la requête est le serveur de shlet des événements manqués.
5) Authentification et autorisation
JWT Bearer dans l'URL de requête (WS) est dangereux (fuites dans les logs). Utilisez le titre (via le handshake HTTP primaire) ou les cookies avec les drapeaux 'Secure', 'HttpOnly', 'SameSite'.
Un mTLS (notamment pour le B2B) ainsi qu'une signature (HMAC) sont possibles au-dessus de la requête initiale.
Pour les SSE avec poupées, souvenez-vous de CORS ('Access-Control-Allow-Origin', 'Allow-Credentials').
Rotation du token : ne coupez pas le flash. Passez « expirera bientôt » → le client redécouvrira la connexion avec le nouveau jeton.
6) Format de données et compression
WebSocket : Activer le permessage-deflate prudent (CPU) ; éviter la compression de formats déjà compressés (Proto, Avro). Payload binaire est plus économique que JSON.
SSE : c'est un texte ; Pour les données volumineuses, envoyer un lien vers une ressource REST/gRPC ou des fichiers chunk ; transport gzip pour SSE - approprié, mais surveillez la mise en tampon dans le proxy/CDN.
7) Mise à l'échelle et fan-out
7. 1 Mise à l'échelle horizontale
Gardez l'application sans mémoire aux redémarrages. L'état des connexions est dans la couche avant ; les données proviennent du courtier.
Sticky (hachage par session/user) est nécessaire s'il y a des files d'attente locales.
L'idéal est le front stateless : le nœud ne multiplexe que les abonnements ; les événements proviennent du pub/sub commun.
7. 2 Pub/Sub et courtiers
Pour un fan out large, utilisez Kafka/NATS/Redis Streams.
La couche « Fanout Gateway » s'abonne aux haches et se moque des clients de WS/SSE.
Appliquez une clé de routage (par exemple, 'userId', 'matchId') pour équilibrer la charge entre les noeuds.
8) Configis de fabrication
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 (il est important de désactiver le tampon)
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) Exemples de code
9. 1 Client SSE (navigateur)
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 Serveur 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 (navigateur)
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) Sécurité et limitation
Taux de limitation par connexion et par utilisateur : limitez la fréquence des messages entrants (WS) et le débit sortant (WS/SSE).
Quota en fonction de la durée de vie de la connexion et du trafic total.
Message size limit и max messages/sec.
WAF/filtres de bot à l'étape de handshake ; protection contre le flooding de connexion (beaucoup de connexions courtes).
Isolation par tenant/namespace : pools de ressources distincts.
11) Observabilité
Métriques :- `connections_active`, `connections_new_total`, `bytes_in/out`,
- `messages_in/out_total`, `dropped_messages_total`,
- `reconnects_total`, `latency_delivery_ms{p50,p95,p99}`.
- Logs : IP/UA, userId/tenantId, raison de la fermeture ('close _ code'), durée.
- Tracing : associer les événements à la commande d'origine (ID de corrélation) ; Pour WS, utilisez des spans « virtuels » sur les pamplemousses.
12) Nuances opérationnelles
Terminaison TLS plus proche du client (CDN/edge).
Rotation proactive des connexions (graceful) en cas de déchargements : donnez le drapeau « recadrer ».
Chardonnez sur la clé pour répartir uniformément les canaux « chauds ».
Instantanés d'état pour les abonnés tardifs (snapshot + delta).
Cache de dernière valeur (SSE en particulier) - utile pour le client « froid ».
13) Anti-modèles
Flash de gros morceaux binaires via SSE ou JSON par WS - utilisez le téléchargement HTTP et le lien dans le message.
Autorisation seulement au moment de la connexion et aucune vérification de plume pendant les longues sessions.
Le sticky mondial n'a pas besoin de → le déséquilibre et les noeuds « chauds ».
Les délais/limites désactivés → les connexions dépendantes mangent le pool.
Le tampon SSE Proxy/CDN → « temps réel » se transforme en minutes de retard.
L'absence de sequence/offset → après reconnect, le client perd l'intégrité des données.
14) Chèque de mise en œuvre
- Le modèle sélectionné est WS (bidirectionnel) ou SSE (unilatéral).
- Taimauts, keepalive et reconnect (backoff + jitter) sont configurés.
- Conçu par sequence/offset et (pour SSE) 'id '/' Last-Event-ID'.
- Limites définies : taille/vitesse/connexions/quotas, protection contre le DoS.
- Configh proxy/ingress : upgrade, proxy_buffering off (SSE), read/send timeouts.
- Mise à l'échelle : pub/sub-broker, fan-out-gateway, sticky seulement si nécessaire.
- Authentification : transfert sécurisé de token, rotation sans falaise.
- Observabilité : métriques, logs, tracés ; dashboards et alertes.
- Plan de sortie : connexion graceful-drain, signal au client sur la connexion à la plume.
- Journées de jeu : falaises du filet, chute du nodule, surchauffe du courtier, RTT longs.
15) FAQ
Est-il possible de mettre en cache un SSE via un CDN ?
Habituellement pas : c'est un flux personnalisé. Pour les chaînes publiques - peut-être avec TTL court et chunked-delivery, mais il est facile de faire « temps réel ».
WebSocket fonctionne-t-il sur le HTTP/2/3 ?
Le navigateur WS démarre avec le HTTP/1. 1-upgrade; il y a la RFC 8441 pour h2, le support dans le proxy/serveur est requis séparément. Avec h3 - en mouvement ; pour le streaming, h3 a WebTransport, mais c'est une autre API.
gRPC vs WS pour le navigateur ?
Un navigateur propre ne dit pas gRPC ; vous avez besoin de gRPC-Web via Envoy. Pour les IU interactifs, WS + REST est souvent plus simple.
16) Résultats
WebSocket - lorsque vous avez besoin d'un dialogue en temps réel et d'un canal bidirectionnel compact.
SSE - lorsque vous avez besoin d'un push simple et fiable de serveur à client, une complexité minimale et une reconnaissance automatique.
Le succès de la vente est le délai et les limites corrects, la restauration offset, pub/sub-fan-out, les réglages corrects du proxy et l'observation claire.