Mit MQTT Wildcards (Platzhaltern) deckt eine einzige Subscription viele Topics ab: + matcht genau eine Topic-Ebene, # beliebig viele Ebenen am Ende eines Filters. In diesem Leitfaden lernen Sie die genauen Matching-Regeln, die Sonderfälle, über die viele stolpern, und eine Reihe von mosquitto_sub-Befehlen, die Sie gegen einen öffentlichen Broker ausführen können, um jeden Filter in Aktion zu sehen.
Topics und Topic-Filter
Publisher veröffentlichen immer auf einem konkreten Topic-Namen, etwa home/kitchen/temperature. Subscriber abonnieren mit einem Topic-Filter, der Wildcards enthalten darf. Der Broker vergleicht jedes eingehende Topic Ebene für Ebene mit dem Filter jeder Subscription und stellt die Nachricht jedem Treffer zu. Dieses Routing ist der Kern dessen, was ein MQTT-Broker tut.
Wildcards sind nur in Filtern gültig. Auf einem Topic zu veröffentlichen, das + oder # enthält, ist ein Protokollfehler, und die meisten Broker trennen den Client daraufhin.
Die einstufige Wildcard: +
+ matcht genau eine Ebene, egal mit welchem Inhalt. Sie muss eine ganze Ebene einnehmen: home/+/temperature ist gültig, home/kit+/temperature nicht.
| Filter | Matcht | Matcht nicht |
|---|---|---|
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 |
Zwei Details sollten Sie sich merken. Erstens matcht + auch eine leere Ebene, home/+/temperature matcht also home//temperature. Zweitens erzeugt ein führender Schrägstrich eine leere erste Ebene, sodass +/home auf /home passt. Beides sind gute Gründe, sinnvollen MQTT Topic-Namenskonventionen zu folgen und leere Ebenen ganz zu vermeiden.
Sie können mehrere + in einem Filter verwenden. factory/+/+/temperature matcht jede Temperatur zwei Ebenen unterhalb von factory, ideal für Dashboards, die einen Messwert über eine ganze Flotte anzeigen.
Die mehrstufige Wildcard:
# matcht beliebig viele Ebenen, auch null, und muss das letzte Zeichen des Filters sein und eine ganze Ebene einnehmen.
| Filter | Matcht | Matcht nicht |
|---|---|---|
home/# |
home, home/kitchen, home/kitchen/light/state |
office/kitchen |
home/kitchen/# |
home/kitchen, home/kitchen/temperature |
home/garage/temperature |
# |
Jedes Topic, das nicht mit $ beginnt |
$SYS/... |
home/+/light/# |
home/kitchen/light, home/kitchen/light/brightness |
home/light |
Ungültige Filter sind etwa home/#/temperature (# nicht am Ende), home# (keine ganze Ebene) und home/kitchen#.
Beachten Sie, dass home/# auch das übergeordnete Topic home selbst matcht. Wenn Sie nur Unterebenen wollen, verwenden Sie home/+/#, das mindestens eine Ebene unterhalb von home verlangt.
Wildcards und $-Topics
Topics, die mit $ beginnen, etwa $SYS/broker/..., sind für Broker-Informationen reserviert. Laut Spezifikation darf ein Filter, der mit einer Wildcard (# oder +) beginnt, sie nicht matchen. Um sie zu lesen, müssen Sie explizit abonnieren, zum Beispiel $SYS/#. So wird eine #-Subscription zum Debuggen nicht mit Broker-Statistiken überflutet. Auf einem ConnectMQ-Broker erscheint der Verbindungsstatus zum Beispiel unter $SYS/broker/connection/{clientId}/state. Mehr dazu unter MQTT $SYS Topics.
MQTT Wildcards an einem Live-Broker ausprobieren
Das folgende Experiment nutzt den öffentlichen MQTT-Broker unter broker.connectmq.com, der für Tests und Prototypen gedacht ist, nicht für Produktionsdaten. Ersetzen Sie w1c5 durch eine eigene Zufallszeichenfolge, damit Sie nur Ihre eigenen Nachrichten sehen, und halten Sie die Payloads harmlos, da der öffentliche Bereich für alle lesbar ist.
Veröffentlichen Sie zunächst einen kleinen retained Topic-Baum, damit neue Subscriber ihn sofort erhalten:
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
Führen Sie nun jede Subscription aus und vergleichen Sie die Ausgabe (Strg+C, sobald die Retained Messages erschienen sind):
S="-h broker.connectmq.com -p 1883 -i wc-sub-w1c5 -v"
R="connectmq/demo/w1c5"
# 1. Jede Temperatur direkt unterhalb eines Raums
mosquitto_sub $S -t "$R/home/+/temperature"
# 2. Alles in der Küche
mosquitto_sub $S -t "$R/home/kitchen/#"
# 3. Alles unter home, einschließlich des Topics home selbst
mosquitto_sub $S -t "$R/home/#"
# 4. Nur Unterebenen von home, nicht home selbst
mosquitto_sub $S -t "$R/home/+/#"
# 5. Temperaturen in jedem Gebäude und Raum
mosquitto_sub $S -t "$R/+/+/temperature"
Erwartete Ergebnisse: Filter 1 liefert zwei Topics, Filter 2 zwei, Filter 3 fünf einschließlich home, Filter 4 vier und Filter 5 drei einschließlich des Büroschreibtischs.
Wenn Sie fertig sind, löschen Sie die Retained Messages, indem Sie leere Payloads mit gesetztem Retain-Flag veröffentlichen:
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
Wildcards in 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):
# Hilfsfunktion für Topic-Matching: prüfen, zu welchem Filter ein Topic gehört
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()
Eine Nachricht, die auf zwei Ihrer Filter passt, kann je nach Broker einmal oder einmal pro passender Subscription zugestellt werden; die Spezifikation erlaubt beide Verhaltensweisen. Gestalten Sie Ihre Handler so, dass sie damit umgehen können.
Topics so gestalten, dass Wildcards für Sie arbeiten
Wildcards sind nur so nützlich wie der Topic-Baum darunter. Da + und # auf ganzen Ebenen arbeiten, bestimmt jede Entscheidung über die Reihenfolge der Ebenen, welche Fragen ein Subscriber günstig stellen kann. Einige Muster zahlen sich immer wieder aus.
Setzen Sie die Dimension, nach der Sie am häufigsten filtern, nach vorne. Schauen Bediener meist auf jeweils einen Standort, gehört der Standort früh ins Topic, sodass acme/berlin/# ihn mit einem Filter erfasst. Vergleichen sie meist einen Messwert über alle Standorte, bleibt der Messwert die letzte Ebene, damit acme/+/+/+/temperature funktioniert. In einem Unified Namespace (UNS), dessen Topics der ISA-95-Hierarchie folgen, ergibt sich diese Reihenfolge fast von selbst: acme/werk-stuttgart/montage/linie-1/# liefert eine ganze Linie, acme/+/+/+/+/temperatur eine Kennzahl aus jeder Zelle aller Werke.
Halten Sie die Tiefe konsistent. Ein Filter wie acme/+/+/status setzt voraus, dass jedes Gerät genau zwei Ebenen unter dem Namespace liegt. Sind manche Geräte eine Ebene tiefer verschachtelt, fallen sie unbemerkt aus jedem Dashboard heraus, das auf diesem Filter basiert. Konsistente Tiefe macht + vorhersehbar.
Trennen Sie Nachrichtenkategorien in eine eigene Ebene. Mit Zweigen wie .../telemetry/..., .../state/... und .../cmd/... kann ein Gerät acme/berlin/hall2/pump-3/cmd/# abonnieren und erhält nur Befehle, und ein Analysedienst kann nur Telemetrie beziehen, ohne je Steuerverkehr zu sehen.
Kodieren Sie nicht mehrere Fakten in einer Ebene. Eine Ebene wie berlin-hall2-pump3 lässt sich mit Wildcards nicht aufteilen, berlin/hall2/pump3 dagegen schon.
Wildcards und Broker-Last
Filter mit Topics abzugleichen ist die tägliche Arbeit eines Brokers, und gut konstruierte Broker verarbeiten Wildcards effizient. Was Sie selbst steuern, ist der Fan-out: Ein breiter Filter bedeutet mehr zugestellte Nachrichten an Ihren Client, mehr Bandbreite und mehr Verarbeitung. In nutzungsabhängigen oder kontingentierten Tarifen ist ein enger Filter auch der günstigere. Muss ein Backend einen breiten Zweig verarbeiten, skalieren Sie es mit Consumer-Gruppen, statt identische Subscriptions zu vervielfachen.
Häufige Fehler mit MQTT Wildcards
#in der Produktion abonnieren. Das empfängt jede Nachricht auf dem Broker (bzw. Mandanten), verbraucht Bandbreite und kann Tarifkontingente ausreizen. Verwenden Sie den engsten Filter, der funktioniert.- Erwarten, dass
+mehrere Ebenen matcht.home/+/temperaturematcht nichthome/kitchen/sensor1/temperature. Verwenden Sie ein zweites+oder strukturieren Sie die Topics um. #in die Mitte setzen.home/#/temperatureist ungültig, und die Subscription wird abgelehnt.- Vergessen, dass Filter Groß- und Kleinschreibung unterscheiden.
home/+/Temperaturematcht keine kleingeschriebenen Topics. - Überlappende Subscriptions. Überlappungen können zu doppelten Zustellungen mit unterschiedlichen QoS-Stufen führen.
- Limits erreichen. Der öffentliche Broker erlaubt 10 Subscriptions pro Client und 8 Topic-Ebenen, setzen Sie also lieber eine gut platzierte Wildcard ein als viele exakte Subscriptions.
Wenn eine Wildcard-Subscription nichts empfängt
Gehen Sie diese Prüfungen der Reihe nach durch: Bestätigen Sie, dass der Reason Code im SUBACK Erfolg anzeigt und keinen abgelehnten Filter; geben Sie das exakte Topic des Publishers aus und vergleichen Sie es Ebene für Ebene mit Ihrem Filter, einschließlich Groß- und Kleinschreibung und eines eventuellen führenden Schrägstrichs; abonnieren Sie vorübergehend den übergeordneten Zweig mit #, um zu sehen, was tatsächlich veröffentlicht wird; und prüfen Sie schließlich, ob Publisher und Subscriber mit demselben Broker und Mandanten verbunden sind. Die meisten Meldungen à la „Wildcards funktionieren nicht“ entpuppen sich als eine Ebene zu viel oder ein Großbuchstabe.
Wildcards lassen sich auch mit MQTT Shared Subscriptions kombinieren: Ein Filter wie $share/workers/factory/+/telemetry/# verteilt einen ganzen Zweig auf eine Gruppe von Consumern. Wenn Sie zuerst die Protokollgrundlagen möchten, lesen Sie das MQTT-Tutorial.
Topics mit ConnectMQ erkunden
ConnectMQ ist ein Cloud-MQTT-Broker mit Live-Topic-Explorer und einem MQTT-Client im Browser direkt im Portal, sodass Sie Wildcard-Filter ohne Tool-Installation gegen Ihren echten Topic-Baum testen können. Erstellen Sie einen kostenlosen Serverless-Broker, ohne Kreditkarte, und verbinden Sie Ihre Geräte dann mit YOUR_USERNAME, YOUR_PASSWORD und YOUR_CLIENT_ID über TCP, TLS oder WebSockets.
Häufig gestellte Fragen
Was ist der Unterschied zwischen + und # in MQTT?
+ matcht genau eine Topic-Ebene und kann an beliebiger Stelle in einem Filter stehen. # matcht null oder mehr Ebenen und muss das letzte Zeichen des Filters sein.
Kann ich auf einem Topic mit Wildcard veröffentlichen?
Nein. Wildcards sind nur in Subscription-Filtern erlaubt. Auf einem Topic zu veröffentlichen, das + oder # enthält, ist ein Protokollverstoß.
Matcht # die $SYS-Topics?
Nein. Filter, die mit einer Wildcard beginnen, matchen keine Topics, die mit $ beginnen. Abonnieren Sie $SYS/# explizit, um sie zu erhalten.
Matcht home/# das Topic home?
Ja. Die mehrstufige Wildcard matcht auch die übergeordnete Ebene, sodass home/# Nachrichten auf home ebenso empfängt wie alles darunter.



