11 points par GN⁺ 2025-04-13 | 3 commentaires | Partager sur WhatsApp
  • 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 requestId pour 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-Key et 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

  • eventkit est 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 Stream et AsyncObservable
  • 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

 
[Ce commentaire a été masqué.]
 
[Ce commentaire a été masqué.]
 
GN⁺ 2025-04-13
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

    • Dans bien des cas, on n’a pas besoin de WebSockets. Les Server-Sent Events (SSE) sont une solution plus simple. C’est dommage que les SSE n’aient pas reçu plus d’attention
  • 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 => ...)

    • Les requêtes d’upgrade sont déroutantes. C’est agaçant qu’un serveur WebSocket soit embarqué dans un serveur HTTP sans être réellement intégré
    • Au lieu de réutiliser un middleware qui lit headers['authorization'] dans les requêtes WebSocket, il faut accéder à un objet connectionParams qui fait semblant d’être des en-têtes de requête
    • L’API navigateur des WebSockets est plus agréable à manipuler qu’EventSource
  • Le 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

    • L’architecte affirme qu’il faut des WebSockets
    • Le POC n’a besoin ni de XHR ni de WebSockets. C’est un parcours d’achat séquentiel
    • Au final, on finit par livrer des WebSockets inutiles
  • 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 est un protocole plus simple, conçu pour la communication bidirectionnelle. Avec une seule connexion, il est plus facile de contrôler le flux de données. La gestion d’état et la récupération après perte de connexion sont plus simples. L’authentification et le contrôle d’accès deviennent aussi plus simples
  • 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