- WebSocket est utile pour la communication en temps réel, mais n’est pas toujours nécessaire, et des alternatives basées sur HTTP peuvent être plus simples et plus fiables
- En matière de traitement transactionnel, de gestion des connexions et de complexité côté serveur, WebSocket peut entraîner une surcharge excessive
- En utilisant HTTP Streaming et une bibliothèque comme
eventkit, il est possible de gérer la synchronisation en temps réel et le traitement d’événements sans WebSocket
Qu’est-ce que WebSocket ?
- WebSocket est une technologie qui ouvre un canal de communication bidirectionnel persistant entre le client et le serveur
- La connexion démarre via HTTP, mais la communication se poursuit ensuite via un protocole distinct
- Il est souvent utilisé pour créer des applications en temps réel et s’avère utile grâce à sa communication bidirectionnelle
Les messages WebSocket ne sont pas transactionnels
- WebSocket ne garantit pas de lien direct entre une requête et une réponse
- Les commandes de changement d’état et les messages de résultat peuvent arriver mélangés dans le même flux
- Par exemple, si un client modifie un état et qu’une erreur survient, il peut être difficile de savoir à quelle commande cette erreur correspond
- Une solution consiste à inclure un
requestIdpour relier commandes et réponses, mais cela augmente la complexité et le coût de gestion - Il est plus simple d’envoyer les commandes dans un modèle transactionnel via HTTP, et de réserver WebSocket à la diffusion des changements d’état
- Il est possible de séparer l’émission via des requêtes HTTP et la réception via WebSocket ou un autre mécanisme de streaming
La difficulté de gérer le cycle de vie des connexions WebSocket
- Avec WebSocket, il faut gérer soi-même l’ouverture, la fermeture, les erreurs et les reconnexions
- Les exemples de traitement de base dans le navigateur incluent l’ouverture de la connexion, la réception de messages, la gestion des erreurs et les événements de fermeture
- Il faut ajouter une logique de reconnexion, de mise en tampon des messages, d’exponential backoff, etc.
- À l’inverse, avec HTTP, le début et la fin sont clairement définis à l’échelle de chaque requête, ce qui simplifie l’implémentation
- Une gestion complexe du cycle de vie ne se justifie que si l’usage de WebSocket est réellement nécessaire
Augmentation de la complexité du code serveur
- WebSocket doit gérer les requêtes d’upgrade HTTP, ce qui nécessite une logique de handshake supplémentaire
- Il faut valider des en-têtes spécifiques comme
Sec-WebSocket-Keyet renvoyer correctement les en-têtes de réponse - Une fois la connexion WebSocket établie, il faut maintenir un état de réception et d’émission continue des messages, avec des problèmes possibles comme la gestion de frames partielles
- Le débogage et le traitement des erreurs deviennent plus difficiles qu’avec une architecture reposant uniquement sur HTTP
- Les frameworks abstraient une partie du processus, mais la complexité fondamentale demeure
Alternative : HTTP Streaming
- HTTP est à l’origine un protocole qui prend en charge le streaming, permettant de transmettre en temps réel un flux de données plutôt qu’un fichier complet
- Il est possible de remplacer uniquement la partie réception d’un WebSocket existant par du HTTP streaming
- Des générateurs asynchrones permettent de traiter les mises à jour d’état sous forme de flux
- Flux côté serveur
- Les mises à jour d’état sont effectuées dans la fonction qui traite les commandes
- Les clients connectés reçoivent chaque nouvelle valeur via un générateur dès qu’elle est produite
- Les commandes de changement d’état sont envoyées via HTTP POST, et le flux temps réel est souscrit via une requête GET
- Flux côté client
- Réception des données en temps réel via la Fetch API et un Stream Reader
- Décodage du texte puis mise à jour de l’interface utilisateur
- Cette structure permet d’implémenter une synchronisation d’état en temps réel sans WebSocket
Bonus : présentation de la bibliothèque eventkit
eventkitest une bibliothèque qui facilite la composition et l’observation de flux asynchrones- Elle est similaire à RxJS, mais améliore la gestion des effets de bord et repose sur des générateurs
- En poussant les mises à jour d’état dans un flux, le client peut les recevoir en temps réel
- Une implémentation simple est possible côté serveur comme côté client via
StreametAsyncObservable - Utilisation d’eventkit côté serveur
- Les changements d’état sont poussés dans un Stream, et les clients s’abonnent à ce flux
- Utilisation d’eventkit côté client
- Les données du flux sont reçues, décodées puis utilisées pour mettre à jour l’interface
- Un dépôt GitHub officiel et un guide HTTP Streaming sont également disponibles
> GitHub: https://github.com/hntrl/eventkit
3 commentaires
Avis sur Hacker News
Je ne pense pas que le streaming HTTP ait été conçu pour ce type de schéma. Le streaming HTTP sert à découper de gros volumes de données en morceaux. Si vous l’utilisez comme mécanisme de pub/sub, vous risquez de le regretter. Les intermédiaires HTTP ne s’attendent pas à ce type de trafic (NGINX, CloudFlare, etc.). À chaque coupure du Wi‑Fi, l’API fetch risque de remonter une erreur d’échec de requête
Envoyer un RequestID au serveur pour obtenir un cycle requête/réponse n’a rien d’étrange ni d’excessif. Dans une application sérieuse, cela vaut toujours la peine d’avoir une API comme
send(message).then(res => ...)headers['authorization']dans les requêtes WebSocket, il faut accéder à un objetconnectionParamsqui fait semblant d’être des en-têtes de requêteLe streaming vidéo fonctionne avec des requêtes de chunks par plage envoyées par le client, pas avec une seule connexion HTTP
Mieux vaut utiliser SSE plutôt qu’EventKit
Dans le POC, je vais utiliser un envoi de formulaire HTTP traditionnel. Rien d’autre n’est nécessaire
Le problème de HTTP/2, c’est que le server push a été ajouté au-dessus d’un protocole existant. HTTP est un protocole de transfert de ressources, ce qui ajoute de l’overhead inutile. L’objectif principal de HTTP/2 est de permettre au serveur de pousser à l’avance des fichiers/ressources au client pour réduire la latence aller-retour
WebSockets n’envoie pas sous forme de flux, mais sous forme de datagrammes (paquets). L’API WebSockets des bibliothèques JavaScript ne sait pas gérer la backpressure et ne peut pas traiter toutes les erreurs. Il faut être prudent si on veut l’utiliser comme un flux TCP
J’ai regretté d’avoir déployé WebSockets en production. Il y avait des problèmes comme NGINX qui coupait les connexions après 4/8 heures, ou le navigateur qui ne se reconnectait pas après une mise en veille. Si possible, il vaut mieux éviter WebSockets et les connexions longues
Il existe une vision idéalisée de WebSockets. On a tendance à vouloir utiliser WebSockets pour les cas d’usage de streaming/temps réel. WebSockets fait perdre la simplicité et les avantages de l’outillage HTTP. Pour les changements côté serveur en streaming, la solution est h2/h3 avec SSE. Si vous pouvez faire du batching jusqu’à 0,5 req/s maximum par client, vous n’avez pas besoin de WebSockets
Les personnes intéressées par le streaming HTTP devraient jeter un œil à Braid-HTTP. Il étend élégamment HTTP au streaming d’événements et fournit un protocole puissant de synchronisation d’état