Tests de brokers

Test de débit MQTT : les messages par seconde expliqués

Réaliser un test de débit MQTT : msg/s entrants et sortants, trouver le débit soutenable, repérer le backpressure et convertir les msg/s en bande passante.

Mis à jour · 9 min de lecture

Test de débit MQTT : les messages par seconde expliqués

« Messages par seconde » est le chiffre MQTT le plus cité et le plus mal compris. Un broker peut accepter 50 000 msg/s des publishers et n'en livrer que la moitié, ou tous les livrer avec deux secondes de retard. Un test de débit MQTT utile mesure le débit que le système peut soutenir — en entrée et en sortie — tant que la latence reste stable et que rien n'est perdu, avec vos propres payloads et votre arborescence de topics.

Ce guide définit les chiffres, montre comment trouver pas à pas le débit soutenable et explique les signaux de backpressure qui indiquent que vous l'avez dépassé. Pour la vue d'ensemble sur les connexions et la latence, voir tests de charge d'un broker MQTT.

Entrée, sortie et fan-out

TermeSignificationOù le mesurer
Débit entrant (ingress)Paquets PUBLISH par seconde acceptés par le broker depuis les publishersCôté publisher (envoyés / acquittés)
Débit sortant (egress)Paquets PUBLISH par seconde livrés par le broker aux subscribersCôté subscriber (reçus)
Facteur de fan-outNombre moyen de subscribers par messageSortie ÷ entrée
Débit soutenableEntrée maximale pour laquelle la sortie suit, la latence est stable et les erreurs restent à zéroDes deux côtés, sur plusieurs minutes

Le facteur de fan-out est ce que l'on oublie. Un publisher à 1 000 msg/s avec 20 subscribers sur le topic représente 20 000 msg/s en sortie. Le travail du broker est plus proche de la sortie que de l'entrée : indiquez donc toujours les deux. Pour les cas extrêmes, voir le test de fan-out et le test de fan-in.

Débit de pointe ou débit soutenable

Tout système peut absorber un moment une rafale au-delà de sa capacité réelle : buffers de socket, files clientes et files du broker se remplissent et masquent le problème. Un test de cinq secondes mesure ces buffers, pas le broker. Le débit soutenable n'apparaît que si vous maintenez chaque palier assez longtemps pour que les files atteignent un régime stable — en général quelques minutes.

La méthode est une rampe par paliers :

  1. Commencez à un débit que le système tient à coup sûr (par exemple 20 % du pic attendu).
  2. Maintenez-le 2 à 5 minutes et relevez entrée, sortie, latence p99 et erreurs.
  3. Augmentez d'un pas fixe (par exemple +25 %) et maintenez à nouveau.
  4. Arrêtez quand la sortie ne suit plus l'entrée, que la latence p99 continue de grimper au sein d'un palier ou que des erreurs apparaissent.
  5. Le dernier palier propre est votre débit soutenable. Dimensionnez la production avec une marge confortable en dessous.

En traçant le débit offert contre la latence, vous obtenez une ligne plate suivie d'un « coude » où la latence s'envole. Ce coude est le chiffre recherché — pas le débit maximal que le générateur de charge a pu pousser.

La rampe avec emqtt-bench

emqtt-bench génère des débits élevés depuis une seule machine. Démarrez d'abord les subscribers, puis les publishers à débit croissant. L'intervalle de publication -I est par client et en millisecondes, le débit total vaut donc clients × 1000 / I :

Subscribers — un par topic de publisher
docker run --rm -it emqx/emqtt-bench sub \
  -h broker.connectmq.com -p 1883 \
  -c 50 -t 'testmqtt/tput/%i' -q 1 \
  -u "$MQTT_USER" -P "$MQTT_PASS"
Publishers — palier 1 : 50 clients × 20 msg/s = 1 000 msg/s
docker run --rm -it emqx/emqtt-bench pub \
  -h broker.connectmq.com -p 1883 \
  -c 50 -I 50 -t 'testmqtt/tput/%i' -s 256 -q 1 \
  -u "$MQTT_USER" -P "$MQTT_PASS"

Pour les paliers suivants, baissez -I (50 → 40 → 30 → 20 → 10 ms) ou ajoutez des clients. emqtt-bench affiche chaque seconde les débits envoyés et reçus ; reportez-les dans un tableur par palier. Comme chaque publisher a ici son propre subscriber, le facteur de fan-out vaut 1 et la sortie doit égaler l'entrée.

Une sonde Python pour les trous et le retard

Les compteurs de débit masquent deux choses : quels messages ont disparu et de combien les autres sont arrivés en retard. Lancez cette petite sonde paho-mqtt 2.x à côté du benchmark. Elle publie des messages numérotés et horodatés à un débit modeste et indique chaque seconde les trous et le retard ; un retard de sonde qui grimpe pendant le bench est votre signal de backpressure :

tput_probe.py
import json, time, uuid
import paho.mqtt.client as mqtt

HOST, PORT = "broker.connectmq.com", 1883
TOPIC = f"testmqtt/tput/probe-{uuid.uuid4().hex[:6]}"
RATE = 100                      # messages de sonde par seconde
stats = {"n": 0, "last": -1, "gaps": 0, "lag": []}

def on_message(client, userdata, msg):
    m = json.loads(msg.payload)
    if m["i"] > stats["last"] + 1:
        stats["gaps"] += m["i"] - stats["last"] - 1
    stats["last"] = max(stats["last"], m["i"])
    stats["n"] += 1
    stats["lag"].append((time.perf_counter() - m["t"]) * 1000)

def client(name):
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"{name}-{uuid.uuid4().hex[:6]}")
    c.connect(HOST, PORT)
    c.loop_start()
    return c

sub = client("probe-sub")
sub.on_message = on_message
sub.subscribe(TOPIC, qos=1)
pub = client("probe-pub")
time.sleep(1)

i, next_report = 0, time.time() + 1
while True:
    pub.publish(TOPIC, json.dumps({"i": i, "t": time.perf_counter()}), qos=1)
    i += 1
    time.sleep(1 / RATE)
    if time.time() >= next_report:
        lag = sorted(stats["lag"]) or [0]
        print(f"rx {stats['n']:>5}/s  gaps {stats['gaps']}  "
              f"lag p50 {lag[len(lag)//2]:.1f} ms  p99 {lag[int(len(lag)*0.99)-1]:.1f} ms")
        stats.update(n=0, lag=[])
        next_report += 1

Un seul processus Python ne générera pas des dizaines de milliers de messages par seconde — c'est le rôle de l'outil de benchmark. La sonde est un thermomètre, pas un radiateur.

Reconnaître le backpressure

MQTT n'a pas de signal global de contrôle de flux ; le backpressure se manifeste par des files qui grossissent quelque part. Sachez où regarder :

SignalOù il se situeCe qu'il signifie
Le débit envoyé du publisher passe sous l'objectifFenêtre inflight du client pleine (QoS 1/2)Les PUBACK reviennent plus lentement que vous ne publiez — broker ou chemin saturé
Le retard de la sonde monte régulièrement au sein d'un palierFile du broker ou du subscriberLa sortie ne suit pas ; vous êtes au-dessus du débit soutenable
Trous dans la sonde en QoS 0Le broker supprime des messages pour les clients lentsLimite de file atteinte ; le broker déleste
Subscribers déconnectés avec erreurs de file ou de quotaLimites par client du brokerVoir file de messages pleine et consommateurs lents
CPU du générateur de charge à 100 %Votre machine de testLe résultat mesure le client, pas le broker

En QoS 1 et 2, la fenêtre inflight est le limiteur principal : un client ne peut avoir qu'un certain nombre de messages non acquittés (max_inflight_messages_set() dans paho, Receive Maximum en MQTT 5). Avec 20 messages en vol et un aller-retour de 50 ms, une connexion plafonne autour de 400 msg/s, quelle que soit la vitesse du broker. C'est pourquoi les tests sur des liens lointains semblent moins bons qu'en local, et pourquoi ce sont davantage de connexions — et non une boucle plus rapide — qui relèvent le plafond. Receive Maximum et contrôle de flux explique le mécanisme.

Des msg/s à la bande passante

Le débit est aussi un budget réseau. Un PUBLISH MQTT coûte à peu près : en-tête fixe (2 à 5 octets) + longueur du topic + 2 octets + identifiant de paquet (2 octets si QoS > 0) + propriétés MQTT 5 + payload. Ajoutez les en-têtes TCP/IP et, le cas échéant, le surcoût des enregistrements TLS. QoS 1 ajoute un PUBACK de 4 octets dans l'autre sens.

Exemple : payload de 256 octets, topic de 20 octets, QoS 1 → environ 285 octets par PUBLISH avant TCP/IP. À 5 000 msg/s en sortie, cela fait environ 1,4 Mo/s, soit à peu près 11 à 12 Mbit/s de trafic MQTT avant le surcoût IP et TLS. Pour les petits messages, le surcoût domine : regrouper plusieurs mesures dans un seul message augmente souvent le débit effectif davantage que n'importe quel réglage du broker. Le calculateur de taille de paquet fait le calcul pour votre topic et votre payload exacts.

Consigner le résultat

Un résultat de débit n'est réutilisable que s'il précise ses conditions. Notez au minimum : version et configuration du broker, QoS, taille du payload, nombre de topics, facteur de fan-out, nombre de connexions publisher et subscriber, TLS activé ou non, durée de chaque palier et région du générateur de charge. Indiquez ensuite les débits soutenables en entrée et en sortie avec leur latence p99, pas le pic. Pour comparer des exécutions plus tard, interpréter les résultats de benchmark couvre les pièges courants.

Avant la rampe, faites un smoke test de vos topics avec le client MQTT en ligne pour valider identifiants et ACL — une erreur de permission sous charge ressemble exactement à un problème de débit.

Un test de débit, c'est la recherche du coude, pas une course au plus gros chiffre. Montez par paliers, maintenez chacun assez longtemps, surveillez retard et trous plutôt que les seuls compteurs, et publiez la sortie à côté de l'entrée — c'est ce chiffre qui permet de dimensionner la capacité.

Questions fréquentes

Quel est un bon débit MQTT ?

Il n'existe pas de chiffre universel : il dépend de la taille du payload, du QoS, du fan-out, de TLS, de la persistance et du matériel. Le chiffre utile est le débit le plus élevé que votre broker soutient avec votre propre profil de trafic, latence stable et sans perte.

Pourquoi mon débit reçu est-il inférieur au débit envoyé ?

Soit les messages s'accumulent quelque part (fenêtres inflight, files du broker, buffers clients), soit ils sont supprimés. Si l'écart grandit avec le temps, vous êtes au-dessus du débit soutenable. En QoS 0, un écart croissant signifie généralement des pertes.

Le QoS influence-t-il le débit MQTT ?

Oui. QoS 1 ajoute un PUBACK par message et par saut, QoS 2 ajoute trois paquets, et tous deux sont limités par la fenêtre inflight. Attendez-vous à des débits de pointe plus faibles avec un QoS élevé, surtout sur des liens à forte latence.