MQTT-Grundlagen

MQTT 5 Subscription-Optionen: No Local, Retain As Published, Retain Handling

Was jede Subscription-Option in MQTT 5 bewirkt, wann sie sinnvoll ist und wie Sie sie in MQTT.js und paho-mqtt setzen, mit lauffähigen Beispielen gegen den öffentlichen MQTT-Broker von ConnectMQ.

Diagramm der MQTT Subscription-Optionen mit den Flags No Local, Retain As Published und Retain Handling in einem SUBSCRIBE-Paket
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

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/room1 und 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/+/config sowohl 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:

  1. 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 mit mosquitto_pub -r.
  2. Starten Sie das Python-Skript. Die Alarm-Subscription verwendet Retain Handling 2, daher wird der retained Alarm nicht zugestellt.
  3. Veröffentlichen Sie einen neuen, nicht retained Alarm. Er kommt sofort an.
  4. Veröffentlichen Sie eine Retained Message auf .../config/rate. Da Retain As Published aktiv ist, gibt das Skript für die Live-Nachricht retain=True aus.
  5. 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: 5 bzw. 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 0x80 oder 0x87 bedeutet, 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.

Verwandte Artikel

Wer hat MQTT erfunden? Die Geschichte von Ölpipelines bis zum IoT

MQTT wurde 1999 von Andy Stanford-Clark (IBM) und Arlen Nipper (Arcom) erfunden, um Ölpipelines per Satellit zu überwachen. Verfolgen Sie den Weg zu Open Source, OASIS, ISO und den heutigen verwalteten MQTT-Brokern in der Cloud.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.