Broker-Tests

MQTT 5 Topic Aliases testen

So testest du MQTT 5 Topic Aliases: Topic Alias Maximum auslesen, Publishes mit Alias prüfen, Ersparnis messen und Topic-Alias-invalid-Fehler erzeugen.

Aktualisiert · 8 Min. Lesezeit

MQTT 5 Topic Aliases testen

MQTT 5 Topic Aliases ersetzen einen langen Topic-Namen für den Rest einer Verbindung durch eine 2-Byte-Zahl. Das erste PUBLISH trägt Topic und Alias. Jedes weitere PUBLISH sendet ein leeres Topic plus den Alias. Zum Testen liest du das Topic Alias Maximum aus, das der Broker im CONNACK gewährt, veröffentlichst mit und ohne Topic-Namen, prüfst, dass Subscriber das volle Topic erhalten, und erzeugst die zwei Fehlerfälle: einen Alias außerhalb des Bereichs und einen Alias, den der Broker nicht kennt.

Den Mechanismus selbst erklärt MQTT Topic Aliases. Dieser Artikel ist der Testplan.

Wie Aliases ausgehandelt werden

RichtungWer das Limit setztWoStandard
Client → BrokerBrokerTopic Alias Maximum im CONNACK0 (keine Aliases)
Broker → ClientClientTopic Alias Maximum im CONNECT0 (keine Aliases)

Regeln, die eine korrekte Implementierung einhält:

  • Alias-Werte reichen von 1 bis zum ausgehandelten Maximum. 0 ist nie gültig.
  • Der Sender wählt die Zuordnung. Ein neues Topic mit einer bestehenden Alias-Nummer ordnet diesen Alias neu zu.
  • Zuordnungen gelten pro Verbindung und pro Richtung. Sie verschwinden beim Disconnect, auch bei persistenter Session.
  • Ein PUBLISH mit leerem Topic muss einen Alias tragen, der auf dieser Verbindung bereits zugeordnet ist.

Wie viel spart ein Alias?

Nimm ein typisches Telemetrie-Topic, factory/line-3/press-07/telemetry/vibration (43 Zeichen), ein 20-Byte-Payload und QoS 1:

FeldVolles TopicMit Alias
Fixed Header2 Bytes2 Bytes
Topic-Name (2-Byte-Länge + Text)45 Bytes2 Bytes (leer)
Packet Identifier2 Bytes2 Bytes
Properties (Länge + Topic Alias)1 Byte4 Bytes
Payload20 Bytes20 Bytes
Summe70 Bytes30 Bytes

Das sind 40 Bytes Ersparnis pro Nachricht, mehr als die Hälfte des Pakets. Bei einer Nachricht pro Sekunde spart ein Gerät etwa 3,5 MB pro Tag. Die Ersparnis zählt nur, wenn Topics im Verhältnis zu den Payloads lang sind und Bytes Geld oder Akku kosten, etwa bei Mobilfunk-, Satelliten- oder LPWAN-Anbindung. In einem LAN mit 2-KB-Payloads ist sie Rauschen. Der Paketgrößenrechner zeigt die Zahlen für deine eigenen Topics, und MQTT Bandbreiten-Overhead behandelt weitere Wege, Traffic zu sparen. TLS bringt eigenen Overhead pro Record mit, miss also auf der Leitung, wenn das Budget knapp ist.

Test 1: das ausgehandelte Maximum auslesen

Logge vor allem anderen, was der Broker gewährt. Broker unterscheiden sich: Manche erlauben viele Aliases, manche nur wenige, und manche schalten sie ab (Maximum 0). Ein Client, der Aliases ohne Prüfung nutzt, wird auf Brokern ohne Alias-Unterstützung getrennt.

Mit der Mosquitto-CLI gibt -d das CONNACK aus, aber die Ausgabe der Properties variiert je nach Version. Ein Skript ist zuverlässiger. Der Test unten gibt zuerst das Maximum aus und bricht ab, wenn es 0 ist.

Test 2: Publishes mit Alias kommen vollständig an

Dieses paho-mqtt-2.x-Skript veröffentlicht eine Nachricht mit vollem Topic und Alias 1, vier weitere mit leerem Topic und Alias 1, und prüft, dass ein normaler Subscriber alle fünf mit vollem Topic-Namen erhält. Danach verbindet es neu und sendet den veralteten Alias, um die Reaktion des Brokers zu sehen.

alias_test.py
import time, uuid
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes

HOST, PORT = "broker.connectmq.com", 1883
RUN = uuid.uuid4().hex[:8]
TOPIC = f"testmqtt/alias/{RUN}/factory/line-3/press-07/telemetry"
received = []

def client(cid, log):
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"{cid}-{RUN}", protocol=mqtt.MQTTv5)
    c.on_connect = lambda cl, u, f, rc, props: log.update(max=getattr(props, "TopicAliasMaximum", 0))
    c.on_disconnect = lambda cl, u, f, rc, props: log.update(disconnect=str(rc))
    return c

def alias_props(alias):
    p = Properties(PacketTypes.PUBLISH)
    p.TopicAlias = alias
    return p

# Subscriber: a normal client that does not accept aliases from the broker
sub_log = {}
sub = client("alias-sub", sub_log)
sub.on_message = lambda cl, u, msg: received.append((msg.topic, msg.payload.decode()))
sub.connect(HOST, PORT)
sub.loop_start()
sub.subscribe(TOPIC, qos=1)

# Test 1: negotiation + valid alias use
log = {}
pub = client("alias-pub", log)
pub.connect(HOST, PORT)
pub.loop_start()
time.sleep(1)
print("broker Topic Alias Maximum:", log.get("max"))
if not log.get("max"):
    raise SystemExit("broker does not accept topic aliases from clients (maximum 0)")

pub.publish(TOPIC, "m0", qos=1, properties=alias_props(1))      # set alias 1 = TOPIC
for i in range(1, 5):
    pub.publish("", f"m{i}", qos=1, properties=alias_props(1))  # topic omitted, alias only
time.sleep(2)
ok = received == [(TOPIC, f"m{i}") for i in range(5)]
print("PASS" if ok else "FAIL", "aliased messages arrive with the full topic:", len(received), "of 5")
pub.disconnect(); pub.loop_stop()

# Test 2: alias mappings do not survive a reconnect
received.clear()
log = {}
pub = client("alias-pub", log)
pub.connect(HOST, PORT)
pub.loop_start()
time.sleep(1)
pub.publish("", "after-reconnect", qos=1, properties=alias_props(1))  # unknown on this connection
time.sleep(2)
print("PASS" if not received else "FAIL", "stale alias after reconnect is not delivered;",
      "broker reaction:", log.get("disconnect", "no DISCONNECT, message dropped"))
pub.disconnect(); pub.loop_stop()

# Test 3: alias above the negotiated maximum (only possible if the maximum is below 65535)
if log.get("max", 0) < 65535:
    log2 = {}
    bad = client("alias-bad", log2)
    bad.connect(HOST, PORT)
    bad.loop_start()
    time.sleep(1)
    bad.publish(TOPIC, "too-high", qos=1, properties=alias_props(log2["max"] + 1))
    time.sleep(2)
    print("broker reaction to alias > maximum:", log2.get("disconnect", "none"))
    bad.loop_stop()

sub.disconnect(); sub.loop_stop()

Ein typischer Lauf gibt aus:

Ausgabe
broker Topic Alias Maximum: 65535
PASS aliased messages arrive with the full topic: 5 of 5
PASS stale alias after reconnect is not delivered; broker reaction: no DISCONNECT, message dropped

paho-mqtt verwaltet Aliases nicht für dich. Es sendet genau den Alias, den du setzt, und löst keine Aliases vom Broker auf, kündige auf einem paho-Subscriber also nie ein Topic Alias Maximum an. Das macht es zugleich zu einem guten Werkzeug für Negativtests. Alias 0 lehnt paho allerdings lokal ab (TopicAlias property value must be in the range 1-65535); um die Reaktion des Brokers auf Alias 0 zu testen, brauchst du ein Tool für rohe Pakete.

Automatische Aliases mit MQTT.js

MQTT.js 5 kann Aliases für dich vergeben. Mit autoAssignTopicAlias ordnet es Topics nach dem Least-Recently-Used-Prinzip Aliases zu, innerhalb des Broker-Maximums. Logge die Pakete, um zu prüfen, was über die Leitung geht:

alias.mjs
import mqtt from "mqtt";

const topic = "testmqtt/alias/js-demo/factory/line-3/press-07/telemetry";
const client = mqtt.connect("mqtt://broker.connectmq.com:1883", {
  protocolVersion: 5,
  clientId: `alias-js-${Math.random().toString(16).slice(2, 10)}`,
  autoAssignTopicAlias: true,             // let MQTT.js pick and reuse aliases
  properties: { topicAliasMaximum: 10 },  // accept up to 10 aliases from the broker
});

// Log what actually goes over the wire
client.on("packetsend", (p) => {
  if (p.cmd === "publish") console.log(`sent topic="${p.topic}" alias=${p.properties?.topicAlias}`);
});

client.on("connect", (connack) => {
  console.log("broker Topic Alias Maximum:", connack.properties?.topicAliasMaximum ?? 0);
  let n = 0;
  const timer = setInterval(() => {
    client.publish(topic, JSON.stringify({ n, vibration: Math.random() }), { qos: 1 });
    if (++n === 5) {
      clearInterval(timer);
      setTimeout(() => client.end(), 500);
    }
  }, 300);
});

client.on("error", (err) => console.error("error:", err.message));

Erwartete Ausgabe: Die erste Zeile zeigt das volle Topic mit alias=1, die nächsten vier topic="" mit alias=1. Zeigt jede Zeile das volle Topic, hat der Broker ein Maximum von 0 gewährt, und MQTT.js ist stillschweigend auf normale Publishes zurückgefallen. Das ist korrekt, bedeutet aber, dass du nichts sparst. autoUseTopicAlias ist die konservativere Option: Sie nutzt nur Aliases wieder, die du selbst registriert hast.

Test 3: die Fehlerfälle

Was du sendestWas die Spezifikation sagtFehler, nach dem gesucht wird
Alias 0ProtokollfehlerDISCONNECT 0x94 Topic Alias invalid
Alias über dem ausgehandelten MaximumProtokollfehlerDISCONNECT 0x94 Topic Alias invalid
Leeres Topic, Alias nie zugeordnetProtokollfehlerDISCONNECT 0x82 Protocol Error
Beliebiger Alias bei Maximum 0Protokollfehler0x94, oder clientseitig Sending Topic Alias out of range in MQTT.js
Veralteter Alias nach ReconnectWie "nie zugeordnet"0x82, oder die Nachricht verschwindet stillschweigend

Nicht alle Broker reagieren gleich. Manche senden das DISCONNECT mit Reason Code, andere schließen die Verbindung oder verwerfen die einzelne Nachricht ohne Rückmeldung. Der Lauf oben zeigt einen Broker, der die Nachricht mit veraltetem Alias verwarf, ohne zu trennen. Dein Test sollte daher das Wesentliche prüfen, die fehlerhafte Nachricht wurde nicht unter einem falschen Topic zugestellt, und die Reaktion des Brokers als Information festhalten. Reason Codes entschlüsselst du mit der Reason-Code-Suche. Die Lösungen für jeden Fall findest du unter Topic-Alias-invalid-Fehler und MQTT-Protokollfehler 0x82.

Die Subscriber-Seite prüfen

Subscriber sollten Aliases nie bemerken, es sei denn, sie haben sich dafür entschieden. Öffne den Online-MQTT-Client, abonniere testmqtt/alias/# und starte eines der Skripte: Jede Nachricht muss das vollständige Topic zeigen. Erhält ein Subscriber ohne angekündigtes Topic Alias Maximum jemals ein leeres Topic, ist der Broker fehlerhaft. Das ist ein seltener, aber ernster Bug, den man melden sollte.

Nachrichten mit Alias mit vollem Topic ankommen sehen

Checkliste

  1. Das Topic Alias Maximum aus jedem CONNACK loggen und nie überschreiten.
  2. Zuerst das volle Topic mit Alias senden, dann Publishes nur mit Alias, und prüfen, dass Subscriber volle Topics sehen.
  3. Neu verbinden und bestätigen, dass keine veralteten Aliases genutzt werden.
  4. Einen Alias über dem Maximum und einen nicht zugeordneten Alias ausprobieren und die Reaktion des Brokers festhalten.
  5. Die echte Byte-Ersparnis für deine Topics messen, bevor du Aliases zur Pflicht machst.

Topic Aliases sind ein günstiger Gewinn auf schmalen Leitungen und überall sonst überflüssig. Teste die Aushandlung und den Reconnect-Fall, dann sparen sie Bandbreite, ohne zum Grund zu werden, warum ein Gerät verstummt.

Häufig gestellte Fragen

Was ist der Standardwert von Topic Alias Maximum?

0. Enthält CONNECT oder CONNACK die Property Topic Alias Maximum nicht, akzeptiert der Empfänger überhaupt keine Aliases. Jede Richtung wird separat ausgehandelt: Der Wert des Clients begrenzt den Broker, der Wert des Brokers begrenzt den Client.

Überleben Topic Aliases einen Reconnect?

Nein. Alias-Zuordnungen existieren nur für die Dauer einer Netzwerkverbindung, auch bei persistenter Session. Nach jedem Reconnect muss der Client den vollständigen Topic-Namen erneut senden, bevor er den Alias nutzen kann.

Sehen Subscriber den Alias?

Nur, wenn sie danach gefragt haben. Aliases gelten pro Hop: Der Broker löst den Alias des Publishers in das volle Topic auf und nutzt Aliases zu einem Subscriber nur, wenn dieser ein eigenes Topic Alias Maximum angekündigt hat. Die meisten Subscriber erhalten einfach den vollen Topic-Namen.