Ein MQTT-Benchmark liefert schnell Zahlen. Zahlen zu liefern, denen du vertrauen kannst, ist schwieriger. Die meisten irreführenden Ergebnisse entstehen durch dieselben wenigen Fehler: Durchschnittswerte zitieren, den Lastgenerator statt des Brokers messen, Coordinated Omission ignorieren und Läufe vergleichen, die nicht gleich aufgesetzt waren.
Dieser Guide geht jede Falle durch, zeigt, wie du sie in deinen eigenen Daten erkennst, und endet mit einer Report-Vorlage, die Ergebnisse reproduzierbar macht. Er setzt voraus, dass du bereits einen Test gefahren hast – falls nicht, beginne mit MQTT-Broker-Lasttests.
Durchschnittswerte verstecken die langsamen Nachrichten
Latenz ist nie normalverteilt. Ein Broker unter Last liefert die meisten Nachrichten in wenigen Millisekunden aus und einen kleinen Teil in Hunderten von Millisekunden – während einer Garbage-Collection-Pause, eines Disk-Flushs oder einer Retransmission. Der Mittelwert vermischt beide Gruppen zu einer Zahl, die keine von beiden beschreibt.
| Lauf | Mittelwert | p50 | p99 | Max |
|---|---|---|---|---|
| A | 6 ms | 4 ms | 12 ms | 30 ms |
| B | 6 ms | 3 ms | 140 ms | 900 ms |
(Beispielwerte.) Beide Läufe haben denselben Durchschnitt, aber in Lauf B kommt jede hundertste Nachricht mehr als eine Zehntelsekunde zu spät an. Bei einem Gerät, das einmal pro Sekunde publiziert, ist das etwa alle zwei Minuten eine spürbare Verzögerung.
Regeln, die dich ehrlich halten:
- Berichte Perzentile: p50 (typisch), p99 (der Tail), Max (der schlimmste beobachtete Fall). Ergänze p99.9 nur, wenn du genug Samples hast.
- Mittle niemals Perzentile über Läufe oder Clients. Der Durchschnitt von zehn p99-Werten ist nicht das p99 der kombinierten Daten – führe stattdessen die Rohdaten oder Histogramme zusammen.
- Prüfe die Stichprobengröße. Das p99 von 100 Nachrichten ist eine einzige Nachricht. Sammle Tausende pro Lauf.
Coordinated Omission
Coordinated Omission ist der häufigste Grund, warum ein Benchmark besser aussieht als die Realität. Sie tritt auf, wenn der Lastgenerator auf eine Antwort wartet, bevor er die nächste Anfrage sendet. Hängt der Broker 500 ms, zeichnet ein synchroner Publisher eine langsame Nachricht auf – und schickt die 49 anderen, die eine echte Flotte in dieser halben Sekunde gesendet hätte, einfach nicht. Diese Nachrichten, die alle hohe Latenz gehabt hätten, tauchen in den Daten nie auf.
Echte Geräte koordinieren sich nicht mit dem Broker. Ein Sensor publiziert nach seinem eigenen Takt, egal ob das letzte PUBACK angekommen ist oder nicht. Um das nachzubilden, miss die Latenz ab dem geplanten Sendezeitpunkt, nicht ab dem Moment, in dem die Nachricht tatsächlich an den Socket übergeben wurde:
import json, statistics, time, uuid
import paho.mqtt.client as mqtt
HOST, PORT = "broker.connectmq.com", 1883
TOPIC = f"testmqtt/bench-{uuid.uuid4().hex[:8]}/latency"
N, RATE = 20000, 500 # Nachrichten, Nachrichten pro Sekunde
samples = []
def on_message(client, userdata, msg):
intended = json.loads(msg.payload)["t"]
samples.append((time.perf_counter() - intended) * 1000)
sub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
sub.on_message = on_message
sub.connect(HOST, PORT)
sub.subscribe(TOPIC, qos=1)
sub.loop_start()
pub = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
pub.max_queued_messages_set(0) # 0 = unbegrenzte Queue im Client
pub.connect(HOST, PORT)
pub.loop_start()
time.sleep(1)
start = time.perf_counter()
for i in range(N):
intended = start + i / RATE # der Takt, dem ein echtes Gerät folgen würde
delay = intended - time.perf_counter()
if delay > 0:
time.sleep(delay)
pub.publish(TOPIC, json.dumps({"i": i, "t": intended}), qos=1)
time.sleep(5)
q = statistics.quantiles(samples, n=1000) # 999 Schnittpunkte
print(f"received {len(samples)}/{N}")
print(f"p50 {q[499]:.1f} ms p99 {q[989]:.1f} ms p99.9 {q[998]:.1f} ms max {max(samples):.1f} ms")Gerät der Publisher in Verzug – weil Broker oder Netzwerk gebremst haben –, trägt jede verspätete Nachricht diese Verzögerung in ihrer Messung. Vergleiche die Ausgabe mit einer Variante, die time.perf_counter() beim Publish stempelt: Ist das p99 mit geplantem Zeitpunkt deutlich höher, hat der Broker gehangen und die naive Messung hat es versteckt.
Misst du den Broker oder den Client?
Eine Benchmark-Maschine, der CPU, Sockets oder Bandbreite ausgehen, erzeugt ein Plateau, das genau wie ein Broker-Limit aussieht. Bevor du dem Broker die Schuld gibst, prüfe den Lastgenerator:
- CPU des Generators. Hängt ein Kern bei 100 % (typisch bei Single-Thread-Clients, etwa einer einzigen paho-Netzwerkschleife mit Hunderten Nachrichten pro Sekunde), liegt die Grenze bei dir. Verteile die Clients auf mehrere Prozesse oder Maschinen.
- Plateaus bei runden Zahlen. Verbindungszahlen, die bei 1.024, 4.096 oder um 28.000–64.000 stehen bleiben, deuten meist auf File-Descriptor- oder Ephemeral-Port-Limits auf dem Client-Host hin. Erhöhe
ulimit -noder nutze weitere Quell-IPs. - Netzwerkinterface. Multipliziere die Nachrichtenrate mit der Größe auf der Leitung (der Paketgrößen-Rechner liefert die exakten Bytes pro PUBLISH) und vergleiche mit der Link-Kapazität – inklusive TLS-Overhead.
- Der Zwei-Maschinen-Test. Fahre dieselbe Last von zwei Generatoren mit jeweils halber Rate. Steigt der Gesamtdurchsatz, war der einzelne Generator der Engpass.
Die Broker-Seite erzählt den Rest: Erfasse CPU, Speicher, Verbindungszahl und Nachrichtenraten (zum Beispiel über $SYS-Topics oder Prometheus) neben den Client-Zahlen. Ein Broker bei 30 % CPU mit festgefahrenem Durchsatz wartet auf etwas anderes.
Uhren, Warm-up und andere stille Verzerrungen
- Clock Skew. Laufen Publisher und Subscriber auf verschiedenen Maschinen, enthält die End-to-End-Latenz die Differenz ihrer Uhren. NTP hält Uhren bestenfalls auf wenige Millisekunden genau – oft schlechter. Miss mit Publisher und Subscriber in einem Prozess oder nutze Round-Trips. Mehr dazu in MQTT-Probleme mit Uhren und Zeitstempeln.
- Warm-up. Die ersten Sekunden enthalten TCP- und TLS-Handshakes, JIT-Kompilierung und Cache-Misses. Verwirf eine Warm-up-Phase oder berichte sie separat.
- Zu kurze Läufe. Ein 30-Sekunden-Lauf verpasst periodische Ereignisse wie Persistenz-Snapshots oder Log-Rotation. Halte jede Laststufe minutenlang; nutze für Stunden einen Soak-Test.
- Verlorene Nachrichten. Perzentile, die nur aus angekommenen Nachrichten berechnet werden, sehen großartig aus, wenn die langsamen verworfen wurden. Berichte immer
empfangen / gesendetneben der Latenz. - Unterschiedliche QoS. QoS 0, 1 und 2 haben unterschiedlich viele Pakete und sehr unterschiedliche Kosten; vergleiche Ergebnisse über QoS-Stufen hinweg nie, als wäre es derselbe Test. Siehe QoS 0 vs 1 vs 2 Benchmark.
Ein schlechtes Ergebnis erkennen
| Symptom in den Daten | Wahrscheinliche Ursache | So bestätigst du es |
|---|---|---|
| Durchsatz flach, Broker-CPU niedrig | Lastgenerator ausgelastet | Generator-CPU prüfen; mit zwei Generatoren wiederholen |
| Empfangen < gesendet bei QoS 0 | Client oder Broker verwirft unter Druck | Mit QoS 1 wiederholen, Zählungen vergleichen |
| p99 gut, Max riesig | Einzelner Hänger (GC, Failover, Disk) | Latenz über die Zeit plotten, nicht nur als Histogramm |
| p50 steigt während des Laufs stetig | Queue staut sich (Slow Consumer) | Queue-/Inflight-Metriken des Brokers beobachten |
| Ergebnisse schwanken 30 % zwischen identischen Läufen | Geteilte oder laute Umgebung | Dreimal laufen lassen; auf isoliertem Broker testen |
| Latenz negativ oder unmöglich niedrig | Uhren verschiedener Maschinen | In einem Prozess messen |
So berichtest du MQTT-Benchmark-Ergebnisse
Eine Zahl ohne ihr Setup lässt sich weder reproduzieren noch vergleichen. Berichte mindestens Folgendes:
| Feld | Beispiel |
|---|---|
| Broker, Version, Deployment | Broker X 2.x, 4 vCPU / 8 GB, Single Node |
| Client-Tool und Version | emqtt-bench / eigenes paho-mqtt-2.x-Skript |
| Lastgenerator | 2 Maschinen, gleiche Region, CPU-Spitze 55 % |
| Netzwerk und Transport | Gleiche Region, TCP 1883 (oder TLS 8883) |
| Protokoll und QoS | MQTT 5, QoS 1, Clean Start |
| Workload | 1.000 Publisher, je 1 msg/s, 512 B JSON, 1:1-Topics |
| Dauer | 5 Min. Warm-up ausgeschlossen, 20 Min. gemessen |
| Ergebnisse | Durchsatz gesendet/empfangen, p50, p99, p99.9, Max, Fehler |
| Rohdaten | Link zu Histogramm oder CSV |
Zwei Gewohnheiten machen Reports deutlich nützlicher:
- Plotte die Latenz über die Zeit. Ein Histogramm verbirgt, wann die langsamen Nachrichten auftraten. Eine Zeitreihe zeigt, ob der Tail eine konstante Eigenschaft ist oder aus wenigen Ereignissen besteht.
- Zeige den Sättigungspunkt. Statt einer einzelnen Zahl fährst du steigende Laststufen und berichtest die Rate, bei der p99 dein Ziel überschreitet (zum Beispiel 50 ms). Diese Rate ist deine nutzbare Kapazität – der absolute Maximaldurchsatz ist es nicht.
Ein kurzer Plausibilitätscheck vor jedem Lauf
Bevor du einen langen Benchmark startest, verbinde dich mit dem Online-MQTT-Client, abonniere dein Test-Topic und publiziere ein paar Nachrichten von Hand. Dauert eine Nachricht sichtbar lange oder empfängt das Abo nichts, behebe das zuerst – ein Lasttest wird dir nicht mehr verraten.
Benchmark-Topic im Browser prüfen
Benchmarks sind nur so gut wie die Fragen dahinter. Miss ab dem geplanten Sendezeitpunkt, behalte den Lastgenerator im Blick, berichte Perzentile zusammen mit Zustellzahlen und dem vollständigen Setup – dann halten deine MQTT-Zahlen auch der nächsten Person stand, die sie reproduzieren will, einschließlich deines zukünftigen Ichs.
Häufig gestellte Fragen
Warum ist die durchschnittliche Latenz eines MQTT-Benchmarks irreführend?
Latenzverteilungen sind schief: Die meisten Nachrichten sind schnell, einige wenige sehr langsam. Der Mittelwert vermischt beides und versteckt genau den langsamen Tail, den Geräte und Nutzer bemerken. Berichte stattdessen p50, p99 und Maximum.
Was ist Coordinated Omission bei einem MQTT-Benchmark?
Sie entsteht, wenn der Lastgenerator auf eine langsame Antwort wartet, bevor er die nächste Nachricht sendet. Der Hänger wird nur einmal gemessen statt für jede Nachricht, die währenddessen hätte gesendet werden sollen – das Tool unterschätzt die Latenz also genau dann, wenn der Broker kämpft.
Wie viele Nachrichten brauche ich für ein verlässliches p99?
Mindestens einige Tausend Samples pro Lauf. Bei 100 Nachrichten ist p99 ein einziges Sample. Ziele auf 10.000 oder mehr für p99 und 100.000 oder mehr, wenn du p99.9 angeben willst.



