Tests de charge

Essais en charge du broker sur deux bancs — par le réseau et en boucle locale — afin de séparer le comportement propre du broker de celui de l'environnement. Ci-dessous : méthode, tableaux complets et conclusions brèves.

Télécharger le rapport (PDF)

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éseauBanc B — boucle locale
Processeur du broker12 cœurs2× Xeon E5-2690 v4 à 2,6 GHz (28 cœurs / 56 fils)
Mémoire du broker16 Go128 Go
Adresse192.168.1.84:1883 (LAN)127.0.0.1:1883 (boucle locale)
Générateur de chargeMachine distincte sur le LANLa même machine que le broker
Ports éphémères du client16 384 (par défaut)55 000 (élargi)
Comment lire la comparaison. Les bancs ne sont pas équivalents et répondent à des questions différentes. Le banc A montre le fonctionnement sur un réseau avec un générateur dédié. Le banc B supprime le réseau (latence quasi nulle) et donne au broker un matériel puissant, mais le générateur et le broker partagent les mêmes cœurs. Latence et fan-out se comparent donc directement, l'ingestion non : sur le banc B, le client vole des cœurs au broker.

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 p50p99
10100146 ms162 ms
1001000702 ms1,12 s
1 0001 00004,21 s9,78 s
5 0005 00004,64 s8,59 s
10 00010 00004,92 s10,52 s
15 00014 8791217,43 s14,09 s

Banc B — boucle locale (montée progressive)

CibleÉtabliesÉchouéesÉtablissement p50p99
1 0001 0000886 ms1,06 s
10 00010 0000903 ms1,55 s
30 00030 0000923 ms1,90 s
50 00041 2190984 ms1,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 un connection 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

ÉditeursBanc A (réseau)Banc B (boucle locale)
10271 006/s185 012/s
100236 422/s101 457/s
500103 546/s125 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

ÉditeursBanc A (réseau)Banc B (boucle locale)
1010 292/s63 606/s
10022 085/s48 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

TailleBanc A (réseau)Banc B (boucle locale)
64 o277 198/s · 17,7 Mo/s101 961/s · 6,5 Mo/s
256 o124 997/s · 32,0 Mo/s102 029/s · 26,1 Mo/s
1 024 o38 458/s · 39,4 Mo/s102 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énarioPublicationLivraisonmoy. / p50 / p99
10 ab. / 10 éd. / 100 msg/s — normal986/s9 863/s1,17 / 1,0 / 3,6 ms
100 ab. / 10 éd. / sans bridage261 551/s22 351/s780 / 453 / 678 ms
100 ab. / 10 éd. / 500 msg/s4 934/s12 150/s1,87 / 1,77 / 2,83 s
1 000 ab. / 5 éd. / 100 msg/s494/s178 961/s2,29 s / 480 / 952 ms

Banc B — boucle locale

ScénarioPublicationLivraisonmoy. / p50 / p99
10 ab. / 10 éd. / 100 msg/s — normal987/s9 867/s73 / ~0 / 555 µs
100 ab. / 10 éd. / sans bridage47 557/s1 353 431/s514 / 135 / 251 ms
100 ab. / 10 éd. / 500 msg/s3 608/s360 834/s17,1 / 10,5 / 71,6 ms
1 000 ab. / 5 éd. / 100 msg/s494/s183 746/s4,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

IndicateurBanc A — réseau (12 cœurs)Banc B — boucle locale (28 cœurs)
Connexions, sans échec10 00030 000
Pic de connexions14 879 (limite du client)~41 000 (limite d'une machine)
Ingestion QoS 0 (64 o), pic271 006/s185 012/s
Ingestion QoS 1 (64 o), pic22 085/s63 606/s
Plafond de volume≈40 Mo/s (liaison)≈105 Mo/s (processeur)
Latence (normale), médiane1,0 ms~0 (73 µs en moyenne)
Livraison en fan-out, pic22 351/s1 353 431/s
Erreurs dans le journal du brokeraucuneaucune

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