Le pub/sub est un pattern de messagerie où des publieurs envoient des messages à des topics sur un broker, et chaque abonné à un topic en reçoit une copie. Toute sa valeur tient à ce que les parties ignorent : les publieurs ne savent jamais qui écoute, les abonnés ne savent jamais qui a envoyé — l’indirection du broker les découple dans l’espace (des systèmes différents), dans le temps (les abonnés peuvent être hors ligne) et dans la synchronisation (la publication rend la main immédiatement). Ajoutez des abonnés sans toucher aux publieurs : c’est le super-pouvoir discret du pattern.
Points clés
| Question | Réponse |
|---|---|
| Les acteurs | Publieur → message → topic (sujet) → broker → abonnés (chacun reçoit une copie) |
| vs. une file d’attente | Une file répartit le travail vers un seul consommateur ; le pub/sub le duplique vers tous |
| vs. observer | L’observer est interne au processus et synchrone ; le pub/sub passe par un broker et est asynchrone |
| La position par défaut | Livraison at-least-once + handlers idempotents |
| Les petites lignes | Pas de réponses, pas d’ordre par défaut, pas de transactions atomiques à travers le broker |
Le pattern en code
Toute API pub/sub se réduit à deux verbes, quel que soit le broker :
// Abonnés — déclarent leur intérêt pour un topic
broker.subscribe('orders.paid', (msg) => fulfill(msg)); // service A
broker.subscribe('orders.paid', (msg) => notifyUser(msg)); // service B — sa propre copie
// Publieur — fire and forget
broker.publish('orders.paid', { orderId: 'o-1187' });
// Le publieur ne sait jamais qui l'a reçu — ni même si quelqu'un l'a reçu.
La même forme côté client, en bordure, où un abonnement live query joue le rôle du topic et une écriture en base de données celui de la publication :
// JavaScript / Node.js — Back4app JS SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
const topic = new Parse.Query('Event');
topic.equalTo('topic', 'orders');
const sub = await topic.subscribe();
sub.on('create', (event) => handle(event.get('payload')));
// Publisher — fire and forget; no knowledge of subscribers
const event = new Parse.Object('Event');
event.set('topic', 'orders');
event.set('payload', { orderId: 'o-1187', status: 'paid' });
await event.save(); // Flutter / Dart — Back4app Flutter SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
final liveQuery = LiveQuery();
final topic = QueryBuilder<ParseObject>(ParseObject('Event'))
..whereEqualTo('topic', 'orders');
final sub = await liveQuery.client.subscribe(topic);
sub.on(LiveQueryEvent.create, (event) => handle(event.get('payload')));
// Publisher — fire and forget; no knowledge of subscribers
final event = ParseObject('Event')
..set('topic', 'orders')
..set('payload', {'orderId': 'o-1187', 'status': 'paid'});
await event.save(); // iOS / Swift — Back4app Swift SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
let topic = Event.query("topic" == "orders")
let sub = try await topic.subscribe()
sub.handleEvent { _, e in
if case .created(let event) = e { handle(event.payload) }
}
// Publisher — fire and forget; no knowledge of subscribers
var event = Event()
event.topic = "orders"
event.payload = ["orderId": "o-1187", "status": "paid"]
try await event.save() // Android / Kotlin — Back4app Android SDK
// Pub/sub at the edge: live queries subscribe, saves publish
// Subscriber — register interest in a topic
val client = ParseLiveQueryClient.Factory.getClient()
val topic = ParseQuery.getQuery<ParseObject>("Event")
topic.whereEqualTo("topic", "orders")
val sub = client.subscribe(topic)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, event ->
handle(event.getJSONObject("payload"))
}
// Publisher — fire and forget; no knowledge of subscribers
val event = ParseObject("Event")
event.put("topic", "orders")
event.put("payload", JSONObject(mapOf("orderId" to "o-1187", "status" to "paid")))
event.save() Comment fonctionne le pub/sub
Le broker assure quatre tâches : accepter les publications et rendre la main immédiatement ; faire correspondre chaque message aux abonnements (par nom de topic, hiérarchiquement — orders.* — ou par attributs de contenu) ; livrer une copie indépendante à chaque abonné, avec retry selon son niveau de garantie ; et conserver les messages pour les abonnés hors ligne quand la durabilité est configurée — le découplage temporel qui permet à un service déployé à midi de rattraper ce qu’il a manqué. Une précision de vocabulaire qui vaut sa phrase : le message est l’enveloppe ; l’événement — « ceci s’est produit », au passé — est ce que le pub/sub transporte en général, et le fait qu’il porte seulement le fait ou l’état complet correspond à la distinction de Fowler entre event notification et event-carried state transfer.
Pub/sub vs. files d’attente de messages vs. pattern observer
Les deux confusions, réglées en un tableau :
| Pub/sub | File d’attente de messages | Observer | |
|---|---|---|---|
| Livraison | Chaque abonné reçoit une copie | Un seul worker consomme chaque message | Le sujet appelle chaque observateur |
| Rôle | Diffuser des événements | Répartir du travail | Réagir à un état dans le processus |
| Couplage | Aucun — via le broker | Aucun — via la file | Références d’objets directes |
| Temporalité | Asynchrone | Asynchrone | Généralement synchrone |
| Périmètre | Entre processus/systèmes | Entre workers | Dans un seul processus |
| Forme canonique | 1 événement → N réactions | N jobs → M workers | 1 objet → ses listeners |
La formule qui survit à la réunion : une file d’attente répartit le travail ; le pub/sub le duplique. Et l’observer n’est pas un « petit pub/sub » — l’indirection du broker est précisément ce qui manque à l’observer, et c’est pourquoi transformer un observer en pub/sub est un changement d’architecture, pas un simple renommage. En pratique, les brokers proposent les deux formes : les consumer groups transforment un topic en file d’attente par groupe (la marque de fabrique de Kafka), les exchanges fanout transforment un système de files en pub/sub.
Garanties de livraison : at-most-once vs. at-least-once vs. exactly-once
| Garantie | Mode de défaillance | Coût | Votre handler doit | À choisir quand |
|---|---|---|---|---|
| At-most-once | Des messages peuvent disparaître | Le moins cher, le plus rapide | Tolérer les trous | Données éphémères : présence, tickers, métriques |
| At-least-once | Des doublons arrivent | Acks + retries | Être idempotent | Le choix par défaut pour les événements métier |
| Exactly-once | Contraint, complexe | Déduplication + coordination | Rester idempotent | Chemins restreints où le broker le permet |
Les niveaux de QoS 0/1/2 de MQTT correspondent exactement à ces trois-là — une confirmation nette que cette trichotomie est fondamentale, pas un argument marketing. La vérité d’ingénieur : l’exactly-once de bout en bout est inatteignable dans le cas général (le problème des deux généraux, avec un badge de broker) ; les systèmes qui l’annoncent combinent une livraison at-least-once et de la déduplication. C’est pourquoi le vrai contrat du pattern s’écrit dans votre handler : at-least-once + idempotence — dédupliquer sur un ID de message, faire des upserts indexés sur l’entité ou vérifier la version avant d’appliquer — transforme les doublons de bugs en no-ops.
Ordre, dead letters et conception des topics
Trois réalités opérationnelles que les schémas d’introduction passent sous silence. L’ordre : le fan-out vers des consommateurs parallèles n’a pas d’ordre global ; les brokers le rétablissent par partition ou par clé d’ordonnancement, au prix du parallélisme — concevez les handlers pour appliquer les événements par ID d’entité et par version, pas par ordre d’arrivée, et le problème se dissout en grande partie. Les messages empoisonnés : un message dont le traitement lève toujours une exception sera retenté indéfiniment en at-least-once ; plafonnez le nombre de tentatives et envoyez les échecs vers une dead-letter queue pour les trier, sinon un seul mauvais événement bloque le topic. La conception des topics : nommez-les hiérarchiquement (orders.paid, orders.refunded, joker orders.*), gardez une granularité au niveau où les abonnés diffèrent réellement, et faites évoluer les schémas de messages de manière additive — les abonnés ignorent les champs inconnus ; les changements cassants reçoivent un nouveau topic versionné (orders.v2) avec une fenêtre de migration, exactement comme une version d’API.
Cas d’usage courants
- Intégration de microservices — des services détenus par des équipes différentes qui réagissent aux événements des autres sans appels directs ni calendriers de déploiement partagés.
- Fonctionnalités temps réel côté client — fan-out de chat, dashboards en direct, présence : du pub/sub sur WebSockets en bordure.
- Télémétrie IoT — des milliers d’appareils qui publient via MQTT ; des consommateurs, de l’alerte à l’analytics, qui s’abonnent indépendamment.
- Invalidation de cache et réplication — une écriture publiée, et chaque cache et chaque réplique prévenus.
- Pipelines de traitement parallèle — un événement qui déclenche en même temps la préparation, la notification et l’analytics, chacun à son rythme.
Devriez-vous utiliser le pub/sub ? Matrice de décision
| Le pub/sub convient quand… | Préférez autre chose quand… |
|---|---|
| Un événement intéresse plusieurs consommateurs indépendants | Un seul consommateur : une file d’attente, ou un simple appel |
| Producteurs et consommateurs se déploient indépendamment | L’appelant a besoin de la réponse tout de suite → requête-réponse |
| La cohérence à terme est acceptable | Atomicité requise entre les parties → transactions/saga |
| Les abonnés vont et viennent au fil du temps | Un ordre global strict est non négociable |
| Une charge en pics doit être amortie entre systèmes | Tout le système tient dans un processus → observer |
Limites et trade-offs
- Le débogage traverse un broker. « Qui a consommé ceci et pourquoi cela a-t-il échoué ? » exige des ID de corrélation et du tracing ; le découplage qui libère les équipes masque aussi la causalité.
- Pas de réponses, par conception. Les workflows qui ont besoin de réponses doivent les modéliser comme des messages supplémentaires — latence en plus et machines à états là où un appel de fonction suffisait.
- Le broker est de l’infrastructure. Disponibilité, capacité, sécurité et politiques de rétention de la couche de messagerie deviennent du travail de plateforme ; un broker en panne multiplie les incidents.
- L’aveuglement du publieur est à double tranchant. Les publieurs ne voient pas la santé des abonnés ; un consommateur qui échoue en silence perd des données s’il n’existe pas de supervision des dead letters.
- Les garanties coûtent du débit. Les clés d’ordonnancement sérialisent, l’exactly-once coordonne, la durabilité persiste — chaque renforcement de la sémantique se paie en performance ; n’achetez que ce dont les données ont besoin.
Le pub/sub sur Back4app
Back4app est une plateforme open-source de Backend as a Service (BaaS) qui combine une base de données gérée, des API REST et GraphQL générées automatiquement, l’authentification, le stockage de fichiers et des fonctions serverless avec Cloud Code. Le pattern apparaît ici côté client, en bordure : les Live Queries sont du pub/sub où la requête est le topic — les abonnés enregistrent un prédicat, et toute écriture qui y correspond est publiée à chaque abonné via un parc de WebSockets géré, avec des permissions vérifiées pour chaque abonné et un fan-out pris en charge par la plateforme, comme dans les onglets de code ci-dessus. Les réactions côté serveur se composent de la même façon : les triggers Cloud Code s’abonnent aux modifications de données (afterSave comme abonnement à un topic, dans l’esprit), et les fonctions planifiées traitent le travail publié sous forme de lignes. Pour du streaming inter-services intensif, vous choisirez toujours un broker dédié comme Kafka ou RabbitMQ — mais pour les cas produit courants, la diffusion à chaque changement est livrée avec le backend.
Questions fréquentes
Qu'est-ce que le pub/sub en termes simples ?
Un pattern de messagerie où des émetteurs (les publieurs) postent des messages dans des topics nommés sur un broker, et où chaque composant abonné à ce topic reçoit sa propre copie — publieurs et abonnés ne se connaissent jamais. Pensez à la radio : la station émet sur une fréquence ; quiconque s'y règle l'entend.
Quelle est la différence entre le pub/sub et une file d'attente de messages ?
Une file d'attente répartit le travail ; le pub/sub le duplique. Dans une file, chaque message est consommé par un seul worker — l'outil pour distribuer des jobs sur un pool. En pub/sub, chaque abonné au topic reçoit une copie — l'outil pour diffuser des événements à des parties intéressées indépendamment les unes des autres.
Quelle est la différence entre le pub/sub et le pattern observer ?
L'observer vit dans un seul processus : le sujet détient des références directes vers ses observateurs et les appelle généralement de manière synchrone. Le pub/sub intercale un broker entre les parties, ce qui les découple totalement, rend l'échange généralement asynchrone et leur permet de vivre dans des processus, des langages et des déploiements différents. L'un est un design pattern interne à une app ; l'autre, une architecture entre systèmes.
Qu'est-ce qu'un topic en pub/sub ?
Un canal logique nommé qui catégorise les messages : les publieurs s'adressent au topic, et le broker livre chaque message à tous les abonnés de ce topic. Le filtrage peut se faire par topic (abonnement par nom, souvent hiérarchique — orders.*) ou par contenu, le broker comparant alors les attributs des messages.
Quelles garanties de livraison le pub/sub offre-t-il ?
Trois niveaux. At-most-once : fire-and-forget — rapide, des messages peuvent se perdre. At-least-once : retry jusqu'à l'accusé de réception — le choix par défaut courant, qui produit des doublons occasionnels. Exactly-once : déduplication plus coordination — coûteux, contraint et impossible à garantir de bout en bout dans le cas général. La position pragmatique : livraison at-least-once avec des handlers idempotents.
Le pub/sub garantit-il l'ordre des messages ?
Pas par défaut — les messages partent en fan-out vers des abonnés parallèles, et un ordre global combat le parallélisme. Les brokers savent ordonner au sein d'une partition ou d'une clé d'ordonnancement, au prix du débit. Une conception robuste ne suppose un ordre que là où il est explicitement configuré et écrit des handlers qui tolèrent les arrivées dans le désordre.
Kafka, est-ce du pub/sub ?
Oui, avec une nuance : Apache Kafka implémente une sémantique de publication-abonnement au-dessus d'un commit log partitionné, durable et rejouable. Au sein d'un consumer group, il se comporte comme une file d'attente (chaque message va à un seul membre) ; entre groupes, comme du pub/sub (chaque groupe reçoit une copie) — c'est pourquoi on parle généralement d'event streaming.
Quand ne pas utiliser le pub/sub ?
Quand l'appelant a besoin d'une réponse immédiate (requête-réponse), quand des opérations doivent être atomiques entre producteur et consommateurs (un broker ne peut pas participer à une transaction — voir le pattern saga), quand un ordre global strict est requis, ou quand le système est assez petit pour qu'un broker ajoute plus de surface opérationnelle qu'il n'en retire.