Le blog

Sortie de Mercure 0.10 !

Publié le 09 juillet 2020

Mercure est un protocole permettant la transmission en temps réel des mises à jour de données basé sur les “Server-sent Events” et tirant parti d’HTTP/2+. Il permet de pousser facilement des messages vers des applications web JavaScript, des applications mobiles ou des objets connectés (IoT). Côté client, il s'appuie sur la classe native EventSource et ne nécessite aucune dépendance JS. Côté serveur, Mercure permet d'utiliser un hub pour gérer les connexions persistantes et distribuer les messages envoyés, par exemple, depuis vos API REST ou GraphQL. Un hub Open Source conçu pour les performances et les charges élevées est développé en parallèle de la spécification. Découvrez le protocole en détails.

Depuis la première version de Mercure, nous portons une attention particulière aux retours de la communauté, des utilisateurs et des contributeurs. Cela nous permet d'identifier les frustrations, les besoins, les limites et les nouveaux cas d'utilisation auxquels nous n’avions pas pensé. Il nous a fallu du temps pour y parvenir, mais les nouvelles versions du protocole et du hub FOSS (écrit en Go) règlent toutes les limitations et les problématiques de conception identifiées jusqu'à présent. Ce sont de loin les versions les plus rapides et efficaces jamais publiées ! Découvrons ensemble ces changements et ces nouvelles fonctionnalités.

mercure new" title="mercure new
Modifications du protocole 
Mécanisme d'autorisation

Le mécanisme d'autorisation est l'une des principales caractéristiques du protocole Mercure. Il permet d'envoyer des mises à jour privées, qui ne seront reçues que par les subscribers disposant des autorisations appropriées. Jusqu'à la dernière version du protocole, ce mécanisme utilisait le concept de targets, et pouvait être difficile à assimiler pour les nouveaux venus. Dans la 6ème version du protocole, le mécanisme d'autorisation a été considérablement repensé et simplifié, tout en restant aussi puissant qu'auparavant.

Le concept de targets a disparu. Désormais, pour envoyer une mise à jour privée, le publisher doit uniquement la marquer comme privée lors de l'envoi de la requête POST au hub en utilisant le nouveau paramètre private.

D'autre part, pour recevoir une mise à jour privée, le subscriber doit présenter au hub un token JWT contenant une liste de sélecteurs de topics (topics selectors) dans le claim mercure.subscribe du token.

Un subscriber ne recevra une mise à jour privée que si :

  • Le topic de la mise à jour est contenu dans la liste des sélecteurs de topics.
  • Le topic correspond à une URI template présente dans la liste.
  • La liste des sélecteurs de topics contient la valeur spéciale “*”, permettant de recevoir toutes les mises à jour.

Des cas d'utilisation plus avancés tels que le "cherry-picking", dont les mises à jour seront reçues par un subscriber, sont également pris en charge grâce aux fonctionnalités existantes telles que les topics alternatifs (alternate topics).

De plus, le nouveau concept de sélecteurs de topics peut également être utilisé pour s'abonner à des mises à jour (publiques ou privées), ce qui rend l'ensemble du protocole plus cohérent. En savoir plus sur les sélecteurs de topics et le mécanisme d'autorisation.

API de présence

L'une des fonctionnalités les plus demandées était la possibilité de récupérer la liste des subscribers actuellement connectés. Cela est maintenant possible grâce à une nouvelle API web exposée par le hub ! Il est aussi possible pour un subscriber de recevoir des mises à jour en provenance de Mercure lorsque d'autres subscribers se connectent ou se déconnectent.

Les hubs peuvent désormais exposer des routes permettant d'obtenir la liste des utilisateurs connectés en tant que documents JSON-LD :

  • /.well-known/subscriptions renvoie la liste de toutes les souscriptions actives
  • /.well-known/subscriptions/{topic} renvoie la liste des souscriptions actives pour un topic spécifique
  • /.well-known/subscriptions/{topic}/{subscriber} renvoie les détails pour une seule souscription

En outre, après avoir récupéré la liste initiale des utilisateurs connectés, les clients peuvent s'abonner (en utilisant le mécanisme de souscription standard) aux mises à jour du statut de connexion en utilisant ces URI comme topics. Pour accéder à cette API, les subscribers doivent être authentifiés et avoir des topic selectors correspondant à ces URI. Enfin, des données arbitraires (comme le nom d'utilisateur ou l'adresse IP de l'abonné) peuvent être jointes à une souscription et récupérées par d'autres subscribers en utilisant le nouveau claim JWT mercure.payload.

L'interface utilisateur de débogage livrée avec le hub de référence prend désormais en charge cette nouvelle fonctionnalité :

Découvrez-en plus sur les abonnements actifs Mercure.

Event Sourcing

Les caractéristiques natives de Mercure en font une solution pratique pour mettre en œuvre l’Event Sourcing. La dernière version du protocole introduit des modifications dédiées à ce type d'usages.

Tout d'abord, il est maintenant possible de définir la valeur de l'en-tête HTTP Last-Event-ID (ou celle du paramètre de requête éponyme) à “earliest” pour récupérer tous les événements passés. Cela permet d'utiliser le hub Mercure de manière similaire à Apache Kafka, mais directement depuis le navigateur (ou tout autre type de client HTTP).

De plus, lorsqu'un en-tête ou un paramètre Last-Event-ID est défini, le hub retournera systématiquement un en-tête Last-Event-ID dans la réponse HTTP. S'il correspond à celui demandé, cela signifie que tous les événements demandés ont été renvoyés, sinon, cela signifie que l'événement demandé peut avoir été supprimé par le hub (s'il stocke un nombre limité d'événements par exemple), ou que cet ID d'événement n'existe pas.

IRIs, Strings, et Fragment Identifiers

Le protocole permet d'utiliser des IRI (recommandé) ou des chaînes de caractères simples (déconseillé mais parfois pratique) pour chaque identifiant : topics, ID d'événements, ID de subscribers...

Les ID d'événements commençant par le caractère # (fragment URI) sont désormais réservés à la génération par le hub. Cela permet à un hub d'utiliser les offsets générés par des systèmes tels que Apache Kafka, Apache Pulsar ou Redis Streams comme identifiants de mise à jour tout en gardant ces identifiants globalement uniques (parce qu'ils sont relatifs à l'URI du hub).

Changements dans l’implémentation de référence 

Toutes les fonctionnalités décrites précédemment ont déjà été implémentées dans l'implémentation de référence (le hub open source écrit en Go) mais ce n'est pas tout ! Il a également été considérablement amélioré au cours des derniers mois.

Nouveau moteur

Jusqu'à la version 0.10, le hub de référence utilisait un moteur fonctionnant de la manière suivante : lorsqu'une nouvelle mise à jour était publiée par un subscriber, un nouveau processus allégé (goroutine) était lancé et la mise à jour était poussée vers chaque subscriber actif dans une boucle. Si un subscriber transmettait un en-tête (ou un paramètre) Last-Event-ID pour demander des événements passés, ceux-ci étaient récupérés dans la couche de persistance et envoyés à partir d'une autre goroutine.

Toutefois, cette approche présentait plusieurs inconvénients :

  • Si un subscriber était lent pour recevoir une mise à jour, cela ralentissait la boucle principale, et donc augmentait la latence également pour les autres subscribers.
  • Si un Last-Event-ID était passé, les nouveaux événements pouvaient être reçus avant les événements provenant de l’historique (par exemple, lorsqu'un nouvel événement était envoyé de la goroutine principale alors qu'une autre goroutine récupérait encore des événements passés de la couche de persistance). L'ordre dans lequel les événements étaient reçus n'était pas garanti à 100%.

Mercure 0.10 contient un tout nouveau moteur qui corrige ces problèmes ! Ce nouveau moteur exploite en profondeur les caractéristiques iconiques du langage de programmation Go : les channels et les goroutines. Désormais, deux tampons de mémoire sont attribués à chaque subscriber : le premier stocke les événements provenant de l'historique tandis que le second stocke les événements "temps réel". Lorsqu'un nouvel événement est publié, la goroutine principale pousse cet événement dans les tampons "temps réel" sans attendre que les événements soient envoyés. Ensuite, une goroutine dédiée à chaque subscriber enlève les événements des tampons qui lui sont associés. Ainsi, chaque subscriber peut recevoir les événements à son propre rythme, sans que cela n'ait d'incidence sur le reste du système.

Ce système tampon corrige également le problème de l'ordre : les goroutines dédiées à des subscribers spécifiques attendent que toutes les mises à jour provenant de l'historique soient envoyées avant de commencer à envoyer les mises à jour stockées dans le tampon "temps réel".

Enfin, ce nouveau système permet de déconnecter les subscribers qui sont trop lents à recevoir les mises à jour. Une nouvelle option de configuration, dispatch_timeout, a été introduite pour configurer le temps maximum qu'un subscriber peut prendre pour recevoir une mise à jour avant d'être déconnecté pour libérer des ressources.

Si vous êtes intéressé par les arcanes du code, jetez un coup d'œil à ce morceau de code en Go !

Un grand merci à Dani Kaplan pour son aide précieuse lors des tests et du débogage de ce nouveau moteur !

Prometheus et support des Health Checks

Prometheus est un système de monitoring et de base de données de séries chronologiques (TSDB) très populaire pour les applications Cloud Native. Jérémy Decool a fait un grand nombre de contributions qui ajoutent le support de Prometheus au hub Mercure. Les métriques comprennent (entre autres) le nombre de subscribers actuellement connectés, le nombre de mises à jour envoyées, le nombre de requêtes regroupées par leur code HTTP, l'utilisation de la mémoire, le nombre actuel de processus et le nombre de descripteurs de fichiers ouverts !

De plus, une nouvelle URL (/healthz) est maintenant disponible pour vérifier si le hub est opérationnel sans polluer les logs HTTP avec des entrées inutiles. C’est particulièrement utile pour les sondes liveness et readiness de Kubernetes.

Flag de version

Jérémy Decool a également apporté un nouveau flag --version permettant de savoir quelle version exacte du Hub est utilisée. Cela peut sembler évident, mais c'était une fonctionnalité manquante. Merci Jérémy de l'avoir ajoutée !

Exemples et démos

Nous avons mis à jour la documentation ainsi que tous les exemples existants (dans divers langages de programmation) pour être en phase avec la dernière version de la spécification. Nous avons également ajouté des exemples et des démos illustrant les nouvelles fonctionnalités, notamment un chat écrit en JavaScript et Python :

La communauté de Mercure

Mercure est de plus en plus populaire, et son intégration avec les écosystèmes existants se fait de plus en plus. Pour cette nouvelle version, nous avons travaillé avec la communauté pour que les bibliothèques et autres outils liés au protocole soient en phase avec les dernières modifications des spécifications. La plupart d'entre eux sont déjà compatibles, y compris : 

Pour les autres intégrations (comme, par exemple, les bibliothèques Java et Python), les Pull Requests sont ouvertes ! Enfin, certains outils doivent encore être mis à jour, comme ce Hub alternatif écrit en Node créé par Nicolas Coutin et l'intégration au framework Yii. Toute aide est la bienvenue !

Aussi, afin de ne manquer aucune mise à jour importante au sujet de Mercure, vous pouvez vous abonner à son compte Twitter !

Améliorer les bibliothèques utilisées par Mercure

L'une des fonctionnalités les plus puissantes de Mercure est la possibilité d'utiliser des templates URI pour souscrire à des topics correspondant à des patterns. Sous le capot, l'implémentation de référence de Mercure utilise la bibliothèque Go uritemplate.

La performance du hub Mercure dépend fortement de la performance de cette bibliothèque. Nous avons alors collaboré avec l'auteur de cette librairie, Kohei Yoshida, pour améliorer ses performances, ajouter des benchmarks et corriger quelques bugs ! Cela a également permis d'améliorer considérablement les performances du hub (et d'autres projets utilisant cette bibliothèque). Merci Kohei pour tes relectures et ton aide !

Le développement de la nouvelle version du hub nous a également permis de trouver un problème dans la bibliothèque standard de Go, et d'ouvrir une PR pour le résoudre.

Montez en compétences sur Mercure

Bonne nouvelle chez Les-Tilleuls.coop, vous pouvez désormais être formé·e au protocole Mercure, consultez notre agenda de disponibilités, des séances sont régulièrement organisées. Notre équipe de consultant·e·s peut également vous accompagner dans sa mise en place à travers des missions d’audit ou de conseil. Contactez notre équipe pour que nous en discutions !

Essayez et soutenez Mercure !

Si vous n’avez jamais essayé Mercure, vous pouvez installer un hub en quelques secondes, ou utiliser la démo en ligne ! Si vous appréciez le projet, montrez votre soutien en lui mettant une étoile sur GitHub !

Cécile Hamerel

Cécile Hamerel

Events & marketing manager

Mots-clésGolang, Mercure, Mercure.rocks

Le blog

Pour aller plus loin