Les wildcards MQTT permettent à un seul abonnement de correspondre à de nombreux topics : + correspond à exactement un niveau de topic et # à un nombre quelconque de niveaux en fin de filtre. Dans ce guide, vous découvrirez les règles précises de correspondance, les cas limites qui piègent les développeurs et une série de commandes mosquitto_sub à lancer sur le serveur MQTT public gratuit pour voir chaque filtre à l'œuvre.
Topics et filtres de topic
Les éditeurs publient toujours sur un nom de topic concret, comme home/kitchen/temperature. Les abonnés s'abonnent avec un filtre de topic, qui peut contenir des wildcards. Le broker compare chaque topic entrant au filtre de chaque abonnement, niveau par niveau, et livre le message à toutes les correspondances. Ce travail de routage est au cœur de ce que fait un broker MQTT.
Les wildcards ne sont valides que dans les filtres. Publier sur un topic contenant + ou # est une erreur de protocole, et la plupart des brokers déconnecteront le client.
Le wildcard à un niveau : +
+ correspond à exactement un niveau, quel que soit son contenu. Il doit occuper un niveau entier : home/+/temperature est valide, home/kit+/temperature ne l'est pas.
| Filtre | Correspond à | Ne correspond pas à |
|---|---|---|
home/+/temperature |
home/kitchen/temperature, home/garage/temperature |
home/temperature, home/kitchen/sensor1/temperature |
+/+/status |
site1/pump3/status |
site1/status, site1/hall/pump3/status |
home/+ |
home/kitchen |
home, home/kitchen/light |
+ |
home |
home/kitchen, /home |
Deux détails méritent d'être retenus. D'abord, + correspond aussi à un niveau vide : home/+/temperature correspond donc à home//temperature. Ensuite, une barre oblique initiale crée un premier niveau vide : +/home correspond donc à /home. Ce sont deux bonnes raisons de suivre des conventions de nommage des topics MQTT sensées et d'éviter totalement les niveaux vides.
Vous pouvez utiliser plusieurs + dans un même filtre. factory/+/+/temperature correspond à toutes les températures situées deux niveaux sous factory, ce qui est idéal pour les tableaux de bord qui affichent une mesure sur l'ensemble d'une flotte.
Le wildcard multi-niveaux :
# correspond à un nombre quelconque de niveaux, y compris zéro, et doit être le dernier caractère du filtre, en occupant un niveau entier.
| Filtre | Correspond à | Ne correspond pas à |
|---|---|---|
home/# |
home, home/kitchen, home/kitchen/light/state |
office/kitchen |
home/kitchen/# |
home/kitchen, home/kitchen/temperature |
home/garage/temperature |
# |
Tous les topics ne commençant pas par $ |
$SYS/... |
home/+/light/# |
home/kitchen/light, home/kitchen/light/brightness |
home/light |
Parmi les filtres invalides figurent home/#/temperature (# n'est pas en dernier), home# (pas un niveau entier) et home/kitchen#.
Notez que home/# correspond aussi au topic parent home lui-même. Si vous ne voulez que les enfants, utilisez home/+/#, qui exige au moins un niveau sous home.
Les wildcards et les topics $
Les topics commençant par $, comme $SYS/broker/..., sont réservés aux informations du broker. La spécification indique qu'un filtre commençant par un wildcard (# ou +) ne doit pas leur correspondre. Pour les lire, vous devez vous abonner explicitement, par exemple à $SYS/#. Cela évite qu'un abonnement # de débogage soit inondé de statistiques du broker. Sur un broker ConnectMQ, par exemple, l'état des connexions apparaît sur $SYS/broker/connection/{clientId}/state. Plus de détails dans les topics $SYS MQTT.
Tester les wildcards MQTT sur un broker réel
L'expérience suivante utilise le serveur MQTT public gratuit broker.connectmq.com, réservé aux tests et aux prototypes. Remplacez w1c5 par votre propre chaîne aléatoire pour ne voir que vos propres messages, et gardez des payloads inoffensifs, car l'espace public est lisible par tous.
Commencez par publier une petite arborescence de topics retenus, afin que les nouveaux abonnés la reçoivent immédiatement :
B="-h broker.connectmq.com -p 1883 -i wc-pub-w1c5 -r -q 1"
R="connectmq/demo/w1c5"
mosquitto_pub $B -t "$R/home/kitchen/temperature" -m 21.5
mosquitto_pub $B -t "$R/home/kitchen/humidity" -m 48
mosquitto_pub $B -t "$R/home/garage/temperature" -m 14.2
mosquitto_pub $B -t "$R/home/garage/door" -m closed
mosquitto_pub $B -t "$R/home" -m house-online
mosquitto_pub $B -t "$R/office/desk/temperature" -m 23.0
Lancez ensuite chaque abonnement et comparez la sortie (Ctrl+C une fois les retained messages affichés) :
S="-h broker.connectmq.com -p 1883 -i wc-sub-w1c5 -v"
R="connectmq/demo/w1c5"
# 1. Toutes les températures directement sous une pièce
mosquitto_sub $S -t "$R/home/+/temperature"
# 2. Tout ce qui concerne la cuisine
mosquitto_sub $S -t "$R/home/kitchen/#"
# 3. Tout ce qui est sous home, y compris le topic home lui-même
mosquitto_sub $S -t "$R/home/#"
# 4. Uniquement les enfants de home, pas home lui-même
mosquitto_sub $S -t "$R/home/+/#"
# 5. Les températures de n'importe quel bâtiment et pièce
mosquitto_sub $S -t "$R/+/+/temperature"
Résultats attendus : le filtre 1 renvoie deux topics ; le filtre 2 en renvoie deux ; le filtre 3 en renvoie cinq, dont home ; le filtre 4 en renvoie quatre ; le filtre 5 en renvoie trois, dont le bureau (office/desk).
Une fois terminé, effacez les retained messages en publiant des payloads vides avec le flag retain :
for t in home/kitchen/temperature home/kitchen/humidity home/garage/temperature \
home/garage/door home office/desk/temperature; do
mosquitto_pub -h broker.connectmq.com -p 1883 -i wc-clean-w1c5 -r -n \
-t "connectmq/demo/w1c5/$t"
done
Les wildcards en Python
import paho.mqtt.client as mqtt
ROOT = "connectmq/demo/w1c5"
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe([(f"{ROOT}/home/+/temperature", 0),
(f"{ROOT}/home/garage/#", 1)])
def on_message(client, userdata, msg):
# Utilitaire de correspondance : vérifier à quel filtre appartient un topic
if mqtt.topic_matches_sub(f"{ROOT}/home/+/temperature", msg.topic):
print("Temperature", msg.topic, msg.payload.decode())
else:
print("Garage", msg.topic, msg.payload.decode())
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="wc-py-w1c5")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()
Un message qui correspond à deux de vos filtres peut être livré une seule fois ou une fois par abonnement correspondant, selon le broker ; la spécification autorise les deux comportements. Concevez vos handlers pour le tolérer.
Concevoir des topics pour que les wildcards travaillent pour vous
Les wildcards ne valent que par l'arborescence de topics sur laquelle ils s'appuient. Comme + et # opèrent sur des niveaux entiers, chaque décision sur l'ordre des niveaux détermine les questions qu'un abonné peut poser à moindre coût. Quelques modèles s'avèrent payants à chaque fois.
Placez en tête la dimension sur laquelle vous filtrez le plus souvent. Si les opérateurs regardent généralement un site à la fois, le site doit figurer tôt dans le topic, pour que acme/berlin/# le capture en un seul filtre. S'ils comparent généralement une mesure sur tous les sites, gardez la mesure comme dernier niveau pour que acme/+/+/+/temperature fonctionne.
Gardez une profondeur cohérente. Un filtre comme acme/+/+/status suppose que chaque appareil se trouve exactement deux niveaux sous l'espace de noms. Si certains appareils sont imbriqués un niveau plus bas, ils disparaissent silencieusement de tous les tableaux de bord construits sur ce filtre. Une profondeur cohérente rend + prévisible.
Séparez les catégories de messages dans leur propre niveau. Avec des branches .../telemetry/..., .../state/... et .../cmd/..., un appareil peut s'abonner à acme/berlin/hall2/pump-3/cmd/# et ne recevoir que les commandes, et un service d'analyse peut ne prendre que la télémétrie sans jamais voir le trafic de contrôle.
N'encodez pas plusieurs informations dans un même niveau. Un niveau comme berlin-hall2-pump3 ne peut pas être découpé par les wildcards ; berlin/hall2/pump3, si.
Wildcards et charge du broker
Faire correspondre des filtres à des topics est le travail quotidien du broker, et les brokers bien conçus gèrent efficacement les wildcards. Le coût que vous maîtrisez est le fan-out : un filtre large signifie davantage de messages livrés à votre client, plus de bande passante et plus de traitement. Sur les offres facturées à l'usage ou soumises à quotas, un filtre étroit est aussi le moins coûteux. Lorsqu'un backend doit traiter une branche large, faites-le monter en charge avec des groupes de consommateurs plutôt qu'en multipliant les abonnements identiques.
Erreurs courantes avec les wildcards MQTT
- S'abonner à
#en production. Cet abonnement reçoit tous les messages du broker (ou du locataire), consomme de la bande passante et peut atteindre les quotas de l'offre. Utilisez le filtre le plus étroit qui fonctionne. - S'attendre à ce que
+corresponde à plusieurs niveaux.home/+/temperaturene correspondra pas àhome/kitchen/sensor1/temperature. Utilisez un second+ou restructurez les topics. - Placer
#au milieu.home/#/temperatureest invalide et l'abonnement sera rejeté. - Oublier que les filtres sont sensibles à la casse.
home/+/Temperaturene correspondra pas aux topics en minuscules. - Abonnements qui se chevauchent. Les chevauchements peuvent produire des livraisons en double à des niveaux de QoS différents.
- Atteindre les limites. Le serveur MQTT public gratuit autorise 10 abonnements par client et 8 niveaux de topic : préférez un wildcard bien placé à de nombreux abonnements exacts.
Quand un abonnement avec wildcard ne reçoit rien
Procédez à ces vérifications dans l'ordre : confirmez que le reason code du SUBACK indique un succès et non un filtre rejeté ; affichez le topic exact utilisé par l'éditeur et comparez-le niveau par niveau à votre filtre, casse et éventuelle barre oblique initiale comprises ; abonnez-vous temporairement à la branche parente avec # pour voir ce qui est réellement publié ; enfin, vérifiez que l'éditeur et l'abonné sont connectés au même broker et au même locataire. La plupart des signalements du type « les wildcards ne marchent pas » se résument à un niveau de trop ou à une majuscule.
Les wildcards se combinent aussi avec les abonnements partagés MQTT : un filtre comme $share/workers/factory/+/telemetry/# répartit toute une branche entre un groupe de consommateurs. Si vous voulez d'abord les bases du protocole, lisez le tutoriel MQTT.
Explorer les topics avec ConnectMQ
ConnectMQ est un broker MQTT managé : ce broker MQTT cloud est doté d'un explorateur de topics en direct et d'un client MQTT intégré au navigateur dans son portail : vous pouvez tester vos filtres avec wildcards sur votre véritable arborescence de topics sans installer d'outils. Créez un broker Serverless gratuit, sans carte bancaire, puis connectez vos appareils avec YOUR_USERNAME, YOUR_PASSWORD et YOUR_CLIENT_ID via TCP, TLS ou WebSockets.
Questions fréquentes
Quelle est la différence entre + et # dans MQTT ?
+ correspond à exactement un niveau de topic et peut apparaître n'importe où dans un filtre. # correspond à zéro niveau ou plus et doit être le dernier caractère du filtre.
Puis-je publier sur un topic contenant un wildcard ?
Non. Les wildcards ne sont autorisés que dans les filtres d'abonnement. Publier sur un topic contenant + ou # constitue une violation du protocole.
Le filtre # correspond-il aux topics $SYS ?
Non. Les filtres qui commencent par un wildcard ne correspondent pas aux topics commençant par $. Abonnez-vous explicitement à $SYS/# pour les recevoir.
Le filtre home/# correspond-il au topic home ?
Oui. Le wildcard multi-niveaux correspond aussi au niveau parent : home/# reçoit les messages publiés sur home ainsi que tout ce qui se trouve en dessous.



