MQTT Subscription-Optionen sind mit MQTT 5.0 eingeführte Flags pro Subscription, die steuern, wie der Broker Ihnen Nachrichten zustellt: ob Sie Ihre eigenen Veröffentlichungen erhalten, ob das Retain-Flag erhalten bleibt und ob Retained Messages beim Abonnieren erneut ausgespielt werden. Nach diesem Artikel wissen Sie, was jede Option bewirkt, welche realen Probleme sie löst und wie Sie sie in JavaScript und Python setzen.
Wo die Subscription-Optionen stecken
In MQTT 3.1.1 enthält ein SUBSCRIBE-Paket einen Topic-Filter und ein Byte mit dem angeforderten QoS. MQTT 5.0 behält dieses Byte bei, nutzt aber die freien Bits für neue Optionen. Jeder Topic-Filter in einem SUBSCRIBE erhält sein eigenes Options-Byte:
| Bits | Option | Werte |
|---|---|---|
| 0–1 | Maximum QoS | 0, 1 oder 2 |
| 2 | No Local | 0 = aus, 1 = an |
| 3 | Retain As Published | 0 = aus, 1 = an |
| 4–5 | Retain Handling | 0, 1 oder 2 |
| 6–7 | Reserviert | muss 0 sein |
Da die Optionen pro Filter gelten, kann ein Client im selben Paket chat/room1 mit aktiviertem No Local und config/# mit anderem Retain-Verhalten abonnieren. Die vollständigen Definitionen stehen in der OASIS-Spezifikation zu MQTT 5.0. Die Optionen gibt es nur, wenn sich der Client mit Protokollversion 5 verbindet; eine 3.1.1-Verbindung kann sie nicht ausdrücken. Zum Rest der Version siehe die Übersicht der MQTT-5-Features.
Maximum QoS
Das ist der bekannte QoS aus MQTT 3.1.1, umbenannt, um seine Bedeutung klarzustellen. Er ist der höchste QoS, mit dem der Broker Nachrichten auf dieser Subscription zustellt. Eine mit QoS 2 veröffentlichte Nachricht an eine Subscription mit Maximum QoS 1 kommt mit QoS 1 an. Eine mit QoS 0 veröffentlichte Nachricht wird nie hochgestuft. Wenn die MQTT-QoS-Stufen neu für Sie sind, lesen Sie zuerst diesen Leitfaden, denn die Herabstufungsregel ist die Quelle vieler Überraschungen bei der Zustellung.
No Local: keine eigenen Nachrichten mehr empfangen
Standardmäßig sendet der Broker eine Nachricht an einen Client zurück, wenn dieser auf einem Topic veröffentlicht, das er selbst abonniert hat. Mit gesetztem No Local leitet der Broker eine Nachricht nicht an die Verbindung weiter, die sie veröffentlicht hat.
Typische Fälle:
- Chat- und Kollaborations-Apps. Jeder Teilnehmer veröffentlicht auf
chat/room1und abonniert es. Ohne No Local sieht jeder Benutzer seine eigene Nachricht doppelt: einmal aus der lokalen Oberfläche und einmal als Echo vom Broker. - Zwei-Wege-Synchronisierung. Ein Dienst, der auf
devices/+/configsowohl veröffentlicht als auch lauscht, würde sonst seine eigenen Updates verarbeiten und möglicherweise in eine Schleife geraten. - Bridges. Eine Bridge, die auf demselben Broker abonniert und erneut veröffentlicht, kann Nachrichtenschleifen erzeugen; No Local auf Seiten der Bridge unterbricht sie.
Zwei Regeln sollten Sie sich merken. No Local wirkt pro Verbindung, identifiziert über die Client-ID; zwei verschiedene Clients derselben Anwendung erhalten also weiterhin die Nachrichten des jeweils anderen. Und es ist ein Protokollfehler, No Local auf einer Shared Subscription ($share/...) zu setzen, weil „lokal“ für eine Gruppe keine Bedeutung hat.
Retain As Published
Wenn der Broker eine Live-Nachricht an Subscriber weiterleitet, löscht er standardmäßig das RETAIN-Flag, selbst wenn der Publisher es gesetzt hat. Das Flag ist nur bei Nachrichten gesetzt, die der Broker aufgrund einer neuen Subscription sendet (die gespeicherte Retained Message). Dieses Standardverhalten erlaubt es Subscribern, „das ist der gespeicherte letzte Wert“ von „das ist gerade passiert“ zu unterscheiden.
Mit aktiviertem Retain As Published behält der Broker das RETAIN-Flag genau so bei, wie der Publisher es gesetzt hat. Das brauchen Sie vor allem, wenn der Subscriber Nachrichten anderswo erneut veröffentlicht und der Retained-Status erhalten bleiben muss:
- Bridges zwischen Brokern, die den Retained-Zustand spiegeln, etwa zwischen einem Edge-Broker in der Werkshalle und einem zentralen Cloud-MQTT-Broker.
- Gateways und Proxys, die an ein zweites System weiterleiten, das selbst Retained Messages berücksichtigt.
- Debugging-Tools, die anzeigen sollen, was der Publisher tatsächlich gesendet hat.
Für gewöhnliche Anwendungs-Subscriber lassen Sie die Option aus.
Retain Handling: Retained-Wiederholungen steuern
Retained Messages werden an einen Client gesendet, wenn er abonniert. Retain Handling entscheidet, wann das geschieht:
| Wert | Verhalten | Einsetzen, wenn |
|---|---|---|
| 0 | Retained Messages bei jedem Subscribe senden (Standard) | Dashboards und Consumer den aktuellen Zustand brauchen |
| 1 | Retained Messages nur senden, wenn die Subscription noch nicht existierte | Clients mit Persistent Sessions beim Reconnect erneut abonnieren |
| 2 | Für diese Subscription nie Retained Messages senden | Ereignisverarbeiter sich nur für neue Nachrichten interessieren |
Wert 1 löst ein subtiles Problem. Ein Gerät mit Persistent Session verbindet sich neu und abonniert vorsichtshalber erneut. Mit Wert 0 erhält es jede Retained Message noch einmal, und handelt es sich um Konfigurationsnachrichten, wendet es sie womöglich erneut an. Mit Wert 1 spielt der Broker Retained Messages nur aus, wenn die Subscription wirklich neu ist.
Wert 2 ist nützlich für Logger und Alarmdienste. Abonniert ein Alarmdienst alarms/# und hält der Broker stundenalte retained Alarme vor, würde Wert 0 bei jedem Neustart alte Alarme auslösen.
In einem Unified Namespace (UNS), dessen Topics der ISA-95-Hierarchie folgen, kombinieren Sie beide Werte gezielt: Ein MES-Konnektor, der beim Start den aktuellen Zustand aller Zellen eines Werks braucht, abonniert etwa acme/werk-stuttgart/+/+/+/status mit Retain Handling 0, während ein Ereignis-Logger für Störmeldungen derselben Anlagen Wert 2 nutzt.
MQTT Subscription-Optionen im Code setzen
Beide Beispiele verbinden sich per TLS und MQTT 5 mit dem öffentlichen MQTT-Broker unter broker.connectmq.com. Verwenden Sie ein eigenes eindeutiges Topic-Präfix; der öffentliche MQTT-Broker ist für Tests gedacht und für alle lesbar, halten Sie die Daten also harmlos.
MQTT.js (Node.js)
// npm install mqtt
const mqtt = require("mqtt");
const prefix = "connectmq/demo/c41e";
const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
clientId: "subopts-js-c41e",
protocolVersion: 5,
});
client.on("connect", () => {
// No Local: eigene Chat-Nachrichten nicht als Echo zurückbekommen
client.subscribe(`${prefix}/chat/room1`, { qos: 1, nl: true }, (err, granted) => {
if (err) return console.error(err);
console.log("chat granted:", granted);
client.publish(`${prefix}/chat/room1`, "hello from js", { qos: 1 });
});
// Retain Handling 2: gespeicherte Alarme ignorieren, nur neue
client.subscribe(`${prefix}/alarms/#`, { qos: 1, rh: 2 });
// Retain As Published: RETAIN-Flag wie gesendet beibehalten
client.subscribe(`${prefix}/config/#`, { qos: 1, rap: true, rh: 1 });
});
client.on("message", (topic, payload, packet) => {
console.log(`${topic} retain=${packet.retain} -> ${payload.toString()}`);
});
Führen Sie es aus, und Sie werden „hello from js“ nicht ausgegeben sehen, weil No Local das Echo unterdrückt. Veröffentlichen Sie von einem anderen Client auf demselben Chat-Topic, erscheint die Nachricht.
Python paho-mqtt 2.x
import ssl
import paho.mqtt.client as mqtt
from paho.mqtt.subscribeoptions import SubscribeOptions
PREFIX = "connectmq/demo/c41e"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("Connect failed:", reason_code)
return
client.subscribe([
(f"{PREFIX}/chat/room1", SubscribeOptions(qos=1, noLocal=True)),
(f"{PREFIX}/alarms/#", SubscribeOptions(
qos=1, retainHandling=SubscribeOptions.RETAIN_DO_NOT_SEND)),
(f"{PREFIX}/config/#", SubscribeOptions(
qos=1, retainAsPublished=True,
retainHandling=SubscribeOptions.RETAIN_SEND_IF_NEW_SUB)),
])
def on_subscribe(client, userdata, mid, reason_code_list, properties):
print("SUBACK:", [str(rc) for rc in reason_code_list])
def on_message(client, userdata, msg):
print(f"{msg.topic} retain={msg.retain} -> {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="subopts-py-c41e", protocol=mqtt.MQTTv5)
client.tls_set(cert_reqs=ssl.CERT_REQUIRED)
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message
client.connect("broker.connectmq.com", 8883, keepalive=60)
client.loop_forever()
Das Verhalten überprüfen
Ein kurzer Testplan, den Sie in wenigen Minuten durchführen können:
- Veröffentlichen Sie von einem beliebigen Client eine Retained Message auf
connectmq/demo/c41e/alarms/pump1, zum Beispiel mit dem MQTT-Client im Browser des ConnectMQ-Portals oder mitmosquitto_pub -r. - Starten Sie das Python-Skript. Die Alarm-Subscription verwendet Retain Handling 2, daher wird der retained Alarm nicht zugestellt.
- Veröffentlichen Sie einen neuen, nicht retained Alarm. Er kommt sofort an.
- Veröffentlichen Sie eine Retained Message auf
.../config/rate. Da Retain As Published aktiv ist, gibt das Skript für die Live-Nachrichtretain=Trueaus. - Starten Sie das Skript neu, ohne etwas anderes zu ändern, und bestätigen Sie, dass das Verhalten reproduzierbar ist.
Häufige Fallstricke
- Verbindung mit MQTT 3.1.1. Bibliotheken ignorieren Subscription-Optionen auf einer 3.1.1-Verbindung stillschweigend. Setzen Sie immer
protocolVersion: 5bzw.protocol=mqtt.MQTTv5. - No Local auf Shared Subscriptions. Der Broker wertet das als Protokollfehler und schließt die Verbindung.
- Erwarten, dass Retain Handling 1 mit Clean Sessions funktioniert. Ist die Session jedes Mal neu, ist auch jede Subscription neu, und Wert 1 verhält sich wie Wert 0.
- Retain As Published in Dashboards verwenden. Das Dashboard kann dann einen gespeicherten Wert nicht mehr von einem frischen unterscheiden. Behalten Sie den Standard bei, sofern Sie Nachrichten nicht weiterleiten.
- Das SUBACK ignorieren. Ein Reason Code wie
0x80oder0x87bedeutet, dass die Subscription abgelehnt wurde. Protokollieren Sie die Reason Codes im SUBACK.
Optionen nach Rolle wählen
| Client-Rolle | Max QoS | No Local | Retain As Published | Retain Handling |
|---|---|---|---|---|
| Dashboard | 1 | aus | aus | 0 |
| Chat-Teilnehmer | 1 | an | aus | 0 oder 2 |
| Alarmdienst | 1 | aus | aus | 2 |
| Gerät mit Persistent Session | 1 | aus | aus | 1 |
| Bridge oder Weiterleitung | 2 | an | an | 0 |
Diese Optionen tragen dazu bei, dass sich auf ConnectMQ und anderen MQTT-5-Brokern leichter aufbauen lässt als auf ihren 3.1.1-Vorgängern: Verhalten, das früher Filterung auf Anwendungsebene erforderte, ist jetzt ein Flag im SUBSCRIBE-Paket. Kombinieren Sie sie mit gutem Topic-Design aus dem Artikel zu MQTT-Wildcards, und halten Sie das MQTT-Tutorial griffbereit, wenn Sie die Grundlagen noch lernen.
Mit ConnectMQ starten
Testen Sie Subscription-Optionen gegen den öffentlichen MQTT-Broker und wechseln Sie dann zu einem privaten verwalteten MQTT-Broker mit MQTT 5.0, TLS und generierten Zugangsdaten. Erstellen Sie einen kostenlosen Serverless-Broker unter app.connectmq.com, ohne Kreditkarte.
Häufig gestellte Fragen
Gibt es MQTT Subscription-Optionen in MQTT 3.1.1?
Nein. MQTT 3.1.1 überträgt nur den angeforderten QoS. No Local, Retain As Published und Retain Handling erfordern eine MQTT-5.0-Verbindung.
Welche Standardwerte haben die Subscription-Optionen?
No Local und Retain As Published sind standardmäßig aus, und Retain Handling steht standardmäßig auf 0, wodurch Retained Messages bei jedem Subscribe gesendet werden. Das entspricht dem Verhalten von MQTT 3.1.1.
Warum erhalte ich trotz aktiviertem No Local meine eigenen Nachrichten?
Prüfen Sie, ob Sie mit MQTT 5 verbunden sind und ob Publisher und Subscriber dieselbe Verbindung sind. Ein zweiter Client, selbst im selben Prozess, ist nicht lokal.
Kann ich Subscription-Optionen ändern, ohne die Subscription zu beenden?
Ja. Ein neues SUBSCRIBE für denselben Topic-Filter ersetzt die Optionen der bestehenden Subscription. Mit Retain Handling 1 werden Retained Messages nicht erneut gesendet, weil die Subscription bereits existiert.



