RPC et PubSub avec WebSocket
WebSocket permet d'échanger des messages dans les deux sens sur une connexion persistante. Il ne définit cependant pas leur signification pour l'application. Lorsqu'un message arrive, comment savoir s'il s'agit d'une demande, d'une réponse ou d'une information envoyée spontanément ?
Les modèles RPC et PubSub permettent de structurer ces échanges. Ils existent aussi avec d'autres transports, mais se retrouvent couramment dans les bibliothèques WebSocket.
RPC : appeler une procédure distante
RPC, pour Remote Procedure Call, consiste à demander à une autre application d'exécuter une procédure et d'en retourner le résultat. L'appelant indique le nom de l'opération et ses paramètres. Le destinataire traite la demande, puis renvoie un résultat ou une erreur.
Par exemple, une interface peut demander au serveur de modifier la température de consigne d'un thermostat. Le serveur vérifie la valeur, applique le changement et confirme la nouvelle consigne. Si la valeur est refusée, il retourne une erreur à l'appelant.
L'appel ressemble à un appel de fonction, mais la demande et la réponse sont des messages qui traversent le réseau. Une bibliothèque peut représenter cet échange par une promesse, ce qui permet d'attendre le résultat avec await et de traiter les erreurs avec try/catch.
Associer une réponse à sa demande
Plusieurs appels RPC peuvent être en cours sur la même connexion. Leurs réponses peuvent arriver dans un ordre différent de celui des demandes, selon le temps nécessaire à chaque traitement.
Pour les associer, on utilise généralement un identifiant d'appel. La demande porte cet identifiant et la réponse reprend la même valeur. La bibliothèque peut ainsi retrouver l'appel en attente et lui transmettre son résultat ou son erreur. Cet identifiant désigne un appel particulier, pas le client qui l'effectue.
Un appel distant peut aussi échouer à cause d'une déconnexion ou dépasser un délai d'attente, appelé timeout. L'absence de réponse ne signifie pas forcément que l'opération n'a pas été exécutée : le serveur peut avoir effectué le changement avant que la connexion soit interrompue. Répéter une opération demande donc de tenir compte de ses effets, par exemple pour éviter de créer deux fois la même commande.
PubSub : publier et s'abonner
PubSub, pour Publish/Subscribe, organise la diffusion de messages autour de sujets ou de canaux.
- Un abonné indique les canaux qu'il souhaite suivre.
- Un producteur publie un message sur un canal.
- Le système de diffusion, souvent le serveur, transmet le message aux abonnés de ce canal.
Le producteur publie sur un canal sans devoir connaître individuellement ses destinataires. Plusieurs clients peuvent recevoir la même publication, et un client peut suivre plusieurs canaux.
Par exemple, un serveur publie les mesures d'un thermostat sur le canal correspondant à la pièce. Chaque interface abonnée reçoit les nouvelles températures dès leur publication. Elle n'a pas besoin de demander périodiquement si une nouvelle mesure est disponible.
Un broadcast global diffuse à tous les clients connectés. Avec PubSub, les abonnements déterminent les destinataires de chaque publication.
Abonnement et état initial
Un abonnement permet de suivre les publications, mais ne fournit pas nécessairement les messages précédents ni l'état actuel. Ces possibilités dépendent du système utilisé. PubSub n'implique pas non plus à lui seul un stockage des messages ou leur remise après une reconnexion.
Une interface peut donc s'abonner aux nouvelles mesures et récupérer séparément la dernière température connue, par exemple avec un RPC. L'application doit coordonner ces échanges pour éviter de manquer une mise à jour pendant son initialisation.
Pourquoi les associer à WebSocket ?
La connexion WebSocket reste ouverte et chaque côté peut envoyer des messages indépendamment de l'autre. Elle peut donc transporter les appels RPC, leurs réponses et les publications PubSub sur une même connexion. Le serveur peut répondre à une demande ou diffuser une information dès qu'un changement se produit.
Les deux modèles répondent à des besoins complémentaires : le RPC permet de demander une opération et d'en attendre le résultat, tandis que le PubSub permet de suivre des événements ou des changements.
Dans l'exemple du thermostat, le RPC confirme personnellement la modification de la consigne à l'interface qui l'a demandée. Une publication peut ensuite informer toutes les interfaces abonnées de cette nouvelle consigne. Les mesures de température continuent, elles aussi, à être diffusées au fil de leur arrivée.
RPC et PubSub sont des conventions définies par l'application ou sa bibliothèque. Leur utilisation au-dessus de WebSocket demande que les deux côtés comprennent le même format de messages.
Représenter les échanges
Un message WebSocket peut contenir du texte ou des données binaires. Pour structurer les échanges, on utilise souvent JSON, qui permet de représenter des objets avec plusieurs propriétés.
Un message peut ainsi indiquer le type d'échange, le nom de la procédure, ses paramètres et l'identifiant de l'appel pour un RPC, ou le nom du canal et les données publiées pour PubSub.
WebSocket transporte ces messages. L'application ou la bibliothèque définit leur structure et interprète leur contenu.
Exemples avec WsMini
La documentation de WsMini présente un exemple RPC et un exemple de chat avec PubSub, avec le code serveur et le code client.