Les bancs
Date : 24 juillet 2026 · Protocole : MQTT 3.1.1 · Outil : client de charge maison cmd/loadtest bâti sur le codec du projet, sans aucune bibliothèque MQTT tierce.
Les essais ont été menés sur deux bancs pour séparer le comportement propre du broker de l'influence de l'environnement : réseau contre boucle locale, matériel modeste contre matériel puissant.
| Banc A — par le réseau | Banc B — boucle locale | |
|---|---|---|
| Processeur du broker | 12 cœurs | 2× Xeon E5-2690 v4 à 2,6 GHz (28 cœurs / 56 fils) |
| Mémoire du broker | 16 Go | 128 Go |
| Adresse | 192.168.1.84:1883 (LAN) | 127.0.0.1:1883 (boucle locale) |
| Générateur de charge | Machine distincte sur le LAN | La même machine que le broker |
| Ports éphémères du client | 16 384 (par défaut) | 55 000 (élargi) |
Synthèse
- Latence. Par le réseau, la latence de bout en bout « éditeur → broker → abonné » sous charge normale est d'environ 1 ms ; en boucle locale, elle est de 73 µs (~16× moins, le réseau ôté).
- Connexions. Par le réseau, le broker a tenu 10 000 sessions sans échec (au-delà, nous avons buté sur la limite de ports du client). Sur matériel puissant en boucle locale : 30 000 sans échec et ~41 000 lors d'une tentative à 50 000 (la limite d'une seule machine, non du broker).
- Ingestion. Le pic par le réseau a été de ≈277 000 messages/s ; en boucle locale l'ingestion est plus faible (≈185 000/s) car le générateur dispute les cœurs au broker.
- Fan-out (distribution). Sur matériel puissant, le broker a distribué ≈1 350 000 messages/s aux abonnés (fan-out ×100) contre ≈22 000/s par le réseau.
- Robustesse. Sur aucun banc le broker n'a planté ni ne s'est figé. En surcharge, les messages QoS 0 destinés aux abonnés en retard sont écartés proprement ; le journal du broker ne contient pas une seule erreur sur l'ensemble des essais.
Capacité en connexions simultanées
Chaque connexion est un cycle complet CONNECT → CONNACK avec authentification ; la session est ensuite maintenue ouverte (vérifiée par PINGREQ).
Banc A — par le réseau
| Cible | Établies | Échouées | Établissement p50 | p99 |
|---|---|---|---|---|
| 10 | 10 | 0 | 146 ms | 162 ms |
| 100 | 100 | 0 | 702 ms | 1,12 s |
| 1 000 | 1 000 | 0 | 4,21 s | 9,78 s |
| 5 000 | 5 000 | 0 | 4,64 s | 8,59 s |
| 10 000 | 10 000 | 0 | 4,92 s | 10,52 s |
| 15 000 | 14 879 | 121 | 7,43 s | 14,09 s |
Banc B — boucle locale (montée progressive)
| Cible | Établies | Échouées | Établissement p50 | p99 |
|---|---|---|---|---|
| 1 000 | 1 000 | 0 | 886 ms | 1,06 s |
| 10 000 | 10 000 | 0 | 903 ms | 1,55 s |
| 30 000 | 30 000 | 0 | 923 ms | 1,90 s |
| 50 000 | 41 219 | 0 | 984 ms | 1,90 s |
Analyse
- Par le réseau, jusqu'à 10 000 — aucun échec ; les 121 échecs à 15 000 sont l'épuisement des ports éphémères du client (16 384). C'est la limite du générateur, non celle du broker.
- En boucle locale, après élargissement de la plage de ports, le broker a tenu 30 000 sessions sans faute ; à 50 000, environ 41 000 restaient vivantes en fin de maintien — les deux côtés partagent une seule machine. Le journal du broker est resté propre du début à la fin, sans erreurs.
- Constat annexe. En boucle locale, une rafale de connexions trop brutale (1 000
dials simultanés) provoque unconnection refused— la file d'attente d'acceptation déborde : sans délai réseau, tous les SYN arrivent en même temps. Une montée progressive (200 à la fois) supprime entièrement l'effet. Sur un réseau, la latence lisse la rafale d'elle-même.
Débit d'ingestion
Un ensemble d'éditeurs publie aussi vite que possible ; on mesure la cadence d'ingestion du broker.
QoS 0 · charge utile de 64 octets
| Éditeurs | Banc A (réseau) | Banc B (boucle locale) |
|---|---|---|
| 10 | 271 006/s | 185 012/s |
| 100 | 236 422/s | 101 457/s |
| 500 | 103 546/s | 125 324/s |
L'ingestion est plus élevée par le réseau — non parce que le broker du banc A serait plus rapide, mais parce que le générateur y tourne sur une machine distincte. En boucle locale, client et broker partagent les mêmes cœurs, ce qui plafonne la cadence globale.
QoS 1 (avec accusé PUBACK) · 64 o
| Éditeurs | Banc A (réseau) | Banc B (boucle locale) |
|---|---|---|
| 10 | 10 292/s | 63 606/s |
| 100 | 22 085/s | 48 983/s |
Le client de test QoS 1 est synchrone (il attend un PUBACK après chaque message) et se trouve donc borné par l'aller-retour. En boucle locale, cet aller-retour est quasi nul, d'où le gain d'environ ×6. Cela montre clairement que les chiffres QoS 1 sont fixés par la latence réseau, non par le broker.
Balayage des tailles de message · QoS 0 · 50 éditeurs
| Taille | Banc A (réseau) | Banc B (boucle locale) |
|---|---|---|
| 64 o | 277 198/s · 17,7 Mo/s | 101 961/s · 6,5 Mo/s |
| 256 o | 124 997/s · 32,0 Mo/s | 102 029/s · 26,1 Mo/s |
| 1 024 o | 38 458/s · 39,4 Mo/s | 102 151/s · 104,6 Mo/s |
Deux plafonds différents :
- Par le réseau, on bute sur la liaison vers ≈40 Mo/s : à mesure que le message grossit, les msg/s tombent tandis que les Mo/s restent près de la limite.
- En boucle locale, la liaison est énorme, donc le nombre de messages tient bon (~102 000/s à 50 connexions) et le volume monte à 104,6 Mo/s. Ici le goulot est le traitement par message (processeur), non les octets.
Livraison de bout en bout
Un horodatage est inséré dans le corps du message ; on mesure la livraison réelle et la latence de bout en bout. Le fan-out désigne la livraison à chaque abonné dont le filtre correspond.
Banc A — par le réseau
| Scénario | Publication | Livraison | moy. / p50 / p99 |
|---|---|---|---|
| 10 ab. / 10 éd. / 100 msg/s — normal | 986/s | 9 863/s | 1,17 / 1,0 / 3,6 ms |
| 100 ab. / 10 éd. / sans bridage | 261 551/s | 22 351/s | 780 / 453 / 678 ms |
| 100 ab. / 10 éd. / 500 msg/s | 4 934/s | 12 150/s | 1,87 / 1,77 / 2,83 s |
| 1 000 ab. / 5 éd. / 100 msg/s | 494/s | 178 961/s | 2,29 s / 480 / 952 ms |
Banc B — boucle locale
| Scénario | Publication | Livraison | moy. / p50 / p99 |
|---|---|---|---|
| 10 ab. / 10 éd. / 100 msg/s — normal | 987/s | 9 867/s | 73 / ~0 / 555 µs |
| 100 ab. / 10 éd. / sans bridage | 47 557/s | 1 353 431/s | 514 / 135 / 251 ms |
| 100 ab. / 10 éd. / 500 msg/s | 3 608/s | 360 834/s | 17,1 / 10,5 / 71,6 ms |
| 1 000 ab. / 5 éd. / 100 msg/s | 494/s | 183 746/s | 4,73 / 4,54 / 15,1 ms |
Analyse
- Latence sous charge normale : 1,17 ms par le réseau contre 73 µs en boucle locale. L'écart, c'est le trajet réseau.
- Distribution sous charge : la machine puissante a livré ≈1,35 million de messages/s aux abonnés (fan-out ×100) — deux ordres de grandeur au-dessus du banc réseau (22 k/s), où le broker comme la liaison sont modestes.
- Dans les scénarios à fort fan-out, le générateur devient lui aussi le goulot (tous les abonnés dans un processus sur une machine), les pics de livraison sont donc une borne inférieure de ce dont le broker est capable.
Face à face
| Indicateur | Banc A — réseau (12 cœurs) | Banc B — boucle locale (28 cœurs) |
|---|---|---|
| Connexions, sans échec | 10 000 | 30 000 |
| Pic de connexions | 14 879 (limite du client) | ~41 000 (limite d'une machine) |
| Ingestion QoS 0 (64 o), pic | 271 006/s | 185 012/s |
| Ingestion QoS 1 (64 o), pic | 22 085/s | 63 606/s |
| Plafond de volume | ≈40 Mo/s (liaison) | ≈105 Mo/s (processeur) |
| Latence (normale), médiane | 1,0 ms | ~0 (73 µs en moyenne) |
| Livraison en fan-out, pic | 22 351/s | 1 353 431/s |
| Erreurs dans le journal du broker | aucune | aucune |
Réserves méthodologiques
- La cadence d'ingestion n'est pas directement comparable entre les bancs : sur le banc B, générateur et broker partagent les cœurs ; sur le banc A, le générateur est une machine distincte.
- Sur chaque banc, la charge vient d'une seule machine cliente ; dans certains scénarios (fan-out important, dizaines de milliers de connexions) c'est cette machine, et non le broker, qui est le goulot.
- Le client de test QoS 1 est synchrone — les chiffres QoS 1 sous-estiment le potentiel du broker.
- Les métriques système du broker (processeur/mémoire pendant les essais) n'ont pas été relevées ; l'état du serveur est déduit de son journal (aucune erreur) et du comportement côté client.
Reproduire
Chaque essai se lance depuis un unique binaire loadtest ; voici les commandes des trois modes.
go build -o loadtest ./cmd/loadtest
# Capacité en connexions (montée progressive contre file d'acceptation)
./loadtest -addr HOST:1883 -user myelx -pass *** \
-mode conn -levels 1000,10000,30000,50000 -hold 3s -dial-concurrency 200
# Débit d'ingestion
./loadtest -addr HOST:1883 -user myelx -pass *** \
-mode pub -pubs 10 -qos 0 -payload 64 -duration 10s
# Livraison de bout en bout et latence
./loadtest -addr HOST:1883 -user myelx -pass *** \
-mode pubsub -subs 10 -pubs 10 -qos 0 -rate 100 -payload 64 -duration 10s