Un benchmark MQTT produit des chiffres très vite. Produire des chiffres fiables est plus difficile. La plupart des résultats trompeurs viennent des mêmes quelques erreurs : citer des moyennes, mesurer le générateur de charge au lieu du broker, ignorer la coordinated omission et comparer des exécutions qui n'ont pas été configurées de la même façon.
Ce guide passe en revue chaque piège, montre comment le repérer dans vos propres données et se termine par un modèle de rapport qui rend les résultats reproductibles. Il suppose que vous avez déjà lancé un test — sinon, commencez par les tests de charge d'un broker MQTT.
Les moyennes cachent les messages lents
La latence ne suit jamais une loi normale. Un broker sous charge délivre la plupart des messages en quelques millisecondes et une petite fraction en centaines de millisecondes — pendant une pause du garbage collector, une écriture disque ou une retransmission. La moyenne mélange les deux groupes en un chiffre qui ne décrit ni l'un ni l'autre.
| Exécution | Moyenne | p50 | p99 | Max |
|---|---|---|---|---|
| A | 6 ms | 4 ms | 12 ms | 30 ms |
| B | 6 ms | 3 ms | 140 ms | 900 ms |
(Valeurs illustratives.) Les deux exécutions ont la même moyenne, mais dans la B un message sur cent arrive avec plus d'un dixième de seconde de retard. Pour un appareil qui publie une fois par seconde, cela fait un retard visible environ toutes les deux minutes.
Des règles qui vous gardent honnête :
- Indiquez des percentiles : p50 (le cas typique), p99 (la queue), max (le pire cas observé). Ajoutez le p99.9 seulement si vous avez assez d'échantillons.
- Ne faites jamais la moyenne de percentiles entre exécutions ou clients. La moyenne de dix valeurs p99 n'est pas le p99 des données combinées — fusionnez les échantillons bruts ou les histogrammes.
- Vérifiez la taille de l'échantillon. Le p99 de 100 messages est un seul message. Collectez-en des milliers par exécution.
La coordinated omission
La coordinated omission est la raison la plus fréquente pour laquelle un benchmark paraît meilleur que la réalité. Elle survient quand le générateur de charge attend une réponse avant d'envoyer la requête suivante. Si le broker se fige 500 ms, un publisher synchrone enregistre un message lent — puis n'envoie tout simplement pas les 49 autres qu'une vraie flotte aurait envoyés pendant cette demi-seconde. Ces messages, qui auraient tous subi une latence élevée, n'apparaissent jamais dans les données.
Les vrais appareils ne se coordonnent pas avec le broker. Un capteur publie selon son propre calendrier, que le dernier PUBACK soit arrivé ou non. Pour reproduire cela, mesurez la latence à partir de l'heure d'envoi prévue, et non du moment où le message a réellement été remis au socket :
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 # messages, messages par seconde
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 = file d'attente illimitée côté client
pub.connect(HOST, PORT)
pub.loop_start()
time.sleep(1)
start = time.perf_counter()
for i in range(N):
intended = start + i / RATE # le calendrier que suivrait un vrai appareil
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 points de coupure
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")Si le publisher prend du retard sur son calendrier — parce que le broker ou le réseau a freiné —, chaque message en retard intègre ce délai dans sa mesure. Comparez la sortie avec une version qui horodate time.perf_counter() au moment du publish : si le p99 basé sur l'heure prévue est nettement plus élevé, le broker se bloquait et la mesure naïve le masquait.
Mesurez-vous le broker ou le client ?
Une machine de benchmark qui manque de CPU, de sockets ou de bande passante produit un plateau qui ressemble exactement à une limite du broker. Avant d'accuser le broker, vérifiez le générateur de charge :
- CPU du générateur. Si un cœur reste à 100 % (fréquent avec des clients mono-thread, comme une seule boucle réseau paho qui traite des centaines de messages par seconde), le plafond vient de chez vous. Répartissez les clients sur plusieurs processus ou machines.
- Plateaux sur des nombres ronds. Des connexions qui s'arrêtent à 1 024, 4 096 ou autour de 28 000–64 000 indiquent généralement une limite de descripteurs de fichiers ou de ports éphémères sur l'hôte client. Augmentez
ulimit -nou ajoutez des IP source. - Interface réseau. Multipliez le débit de messages par la taille sur le fil (le calculateur de taille de paquet donne les octets exacts par PUBLISH) et comparez avec la capacité du lien — surcoût TLS compris.
- Le test à deux machines. Lancez la même charge depuis deux générateurs, chacun à la moitié du débit. Si le débit total augmente, le générateur unique était le goulot d'étranglement.
Le côté broker raconte la suite : relevez son CPU, sa mémoire, son nombre de connexions et ses débits de messages (par exemple via les topics $SYS ou Prometheus) à côté des chiffres côté client. Un broker à 30 % de CPU avec une courbe de débit bloquée attend autre chose.
Horloges, préchauffage et autres distorsions discrètes
- Décalage d'horloge. Si le publisher et le subscriber tournent sur des machines différentes, la latence de bout en bout inclut l'écart entre leurs horloges. NTP maintient les horloges à quelques millisecondes au mieux — souvent moins bien. Mesurez avec publisher et subscriber dans le même processus, ou utilisez des allers-retours. Plus de détails dans problèmes d'horloge et d'horodatage MQTT.
- Préchauffage. Les premières secondes incluent les handshakes TCP et TLS, la compilation JIT et les défauts de cache. Écartez une période de préchauffage ou rapportez-la séparément.
- Exécutions trop courtes. Une exécution de 30 secondes peut manquer des événements périodiques comme les snapshots de persistance ou la rotation des logs. Maintenez chaque palier de charge plusieurs minutes ; utilisez un soak test pour plusieurs heures.
- Messages perdus. Des percentiles calculés uniquement sur les messages arrivés semblent excellents si les lents ont été abandonnés. Indiquez toujours
reçus / envoyésà côté de la latence. - QoS différente. Les QoS 0, 1 et 2 n'ont ni le même nombre de paquets ni le même coût ; ne comparez jamais des résultats entre niveaux de QoS comme s'il s'agissait du même test. Voir le benchmark QoS 0 vs 1 vs 2.
Repérer un mauvais résultat
| Symptôme dans les données | Cause probable | Comment confirmer |
|---|---|---|
| Débit plat alors que le CPU du broker est bas | Générateur de charge saturé | Vérifier le CPU du générateur ; refaire avec deux générateurs |
| Reçus < envoyés en QoS 0 | Client ou broker qui abandonne sous pression | Refaire en QoS 1 et comparer les comptes |
| p99 correct, max énorme | Un blocage isolé (GC, failover, disque) | Tracer la latence dans le temps, pas seulement un histogramme |
| Le p50 monte régulièrement pendant l'exécution | File qui s'accumule (consommateur lent) | Surveiller les métriques de file / inflight du broker |
| Résultats qui varient de 30 % entre exécutions identiques | Environnement partagé ou bruyant | Lancer trois fois ; tester sur un broker isolé |
| Latence négative ou impossiblement basse | Horloges de machines différentes | Mesurer dans un seul processus |
Comment présenter les résultats d'un benchmark MQTT
Un chiffre sans sa configuration ne peut être ni reproduit ni comparé. Indiquez au minimum :
| Champ | Exemple |
|---|---|
| Broker, version, déploiement | Broker X 2.x, 4 vCPU / 8 Go, un seul nœud |
| Outil client et version | emqtt-bench / script maison paho-mqtt 2.x |
| Générateur de charge | 2 machines, même région, pic CPU 55 % |
| Réseau et transport | Même région, TCP 1883 (ou TLS 8883) |
| Protocole et QoS | MQTT 5, QoS 1, clean start |
| Charge | 1 000 publishers, 1 msg/s chacun, JSON de 512 o, topics 1:1 |
| Durée | 5 min de préchauffage exclues, 20 min mesurées |
| Résultats | Débit envoyé/reçu, p50, p99, p99.9, max, erreurs |
| Données brutes | Lien vers l'histogramme ou le CSV |
Deux habitudes rendent les rapports bien plus utiles :
- Tracez la latence dans le temps. Un histogramme cache quand les messages lents se sont produits. Une série temporelle montre si la queue est une propriété constante ou quelques événements.
- Montrez le point de saturation. Au lieu d'un chiffre unique, lancez des paliers de charge croissants et indiquez le débit auquel le p99 dépasse votre objectif (par exemple 50 ms). Ce débit est votre capacité utilisable ; le débit maximal absolu ne l'est pas.
Une vérification rapide avant chaque exécution
Avant de lancer un long benchmark, connectez-vous avec le client MQTT en ligne, abonnez-vous à votre topic de test et publiez quelques messages à la main. Si un message met visiblement du temps ou si l'abonnement ne reçoit rien, corrigez cela d'abord — un test de charge ne vous en dira pas plus.
Tester votre topic de benchmark
Un benchmark ne vaut que par les questions qu'il pose. Mesurez à partir de l'heure d'envoi prévue, surveillez le générateur de charge, présentez les percentiles avec les comptes de livraison et la configuration complète : vos chiffres MQTT résisteront à la prochaine personne qui tentera de les reproduire — vous y compris, dans quelques mois.
Questions fréquentes
Pourquoi la latence moyenne d'un benchmark MQTT est-elle trompeuse ?
Les distributions de latence sont asymétriques : la plupart des messages sont rapides et quelques-uns très lents. La moyenne mélange les deux et masque la queue lente que les appareils et les utilisateurs remarquent. Indiquez plutôt p50, p99 et le maximum.
Qu'est-ce que la coordinated omission dans un benchmark MQTT ?
Elle se produit quand le générateur de charge attend une réponse lente avant d'envoyer le message suivant. Le blocage n'est mesuré qu'une fois au lieu de l'être pour chaque message qui aurait dû partir pendant ce temps : l'outil sous-estime la latence précisément quand le broker est en difficulté.
Combien de messages faut-il pour un p99 fiable ?
Au moins plusieurs milliers d'échantillons par exécution. Avec 100 messages, le p99 correspond à un seul échantillon. Visez 10 000 ou plus pour le p99 et 100 000 ou plus si vous voulez citer le p99.9.



