Tests de brokers

Test de stress des connexions MQTT : combien de clients un broker supporte-t-il ?

Test de stress MQTT : ouvrez des dizaines de milliers de clients, réglez ulimit et ports éphémères, multipliez les IP source et simulez des reconnexions.

Mis à jour · 9 min de lecture

Test de stress des connexions MQTT : combien de clients un broker supporte-t-il ?

Un test de stress des connexions MQTT répond à une question simple : combien de clients ce broker peut-il maintenir simultanément, et à quelle vitesse peut-il les accepter ? La méthode consiste à augmenter des connexions inactives à un rythme contrôlé tout en surveillant la mémoire, le CPU et la latence de connexion du broker — et à vérifier que la limite atteinte appartient bien au broker, pas à la machine qui génère la charge.

La plupart des premières tentatives s'arrêtent sur un chiffre étrangement rond. Ce guide montre comment lever ces limites côté client, mener le test avec emqtt-bench ou un script Node.js, et simuler la tempête de reconnexions qui suit chaque redémarrage du broker.

Ce que vous mesurez vraiment

MétriquePourquoi elle compte
Connexions simultanées maximalesTaille de flotte que le broker peut maintenir au repos
Débit de connexion (CONNECT/s)Vitesse à laquelle une flotte revient après une panne
Latence de connexion (p50/p99)Temps entre le SYN TCP et le CONNACK sous charge
Mémoire du broker par connexionPlanification de capacité ; mesurez, ne devinez pas
Types d'erreursRefus, timeouts, reason codes du CONNACK

Les connexions coûtent au broker de la mémoire pour les buffers de socket, l'état de session et les abonnements, ainsi que du CPU pour les handshakes TLS et le traitement du keep-alive. Tout cela dépend du broker, de sa version et de sa configuration : le seul chiffre pertinent est celui que vous mesurez avec vos réglages — même TLS, même keep-alive, même authentification et à peu près le même nombre d'abonnements par client.

Corriger d'abord le générateur de charge

Une seule machine Linux a trois limites qui plafonnent les connexions sortantes bien avant qu'un broker correct ne cède.

Descripteurs de fichiers

Chaque socket TCP est un descripteur de fichier. La limite souple par défaut est souvent de 1 024 :

Vérifier et augmenter la limite de fichiers ouverts
ulimit -n                 # limite souple actuelle
ulimit -n 200000          # l'augmenter pour ce shell (jusqu'à la limite dure)
cat /proc/sys/fs/file-max # plafond du système

Pour un changement permanent, définissez nofile dans /etc/security/limits.conf ou, avec Docker, passez --ulimit nofile=200000:200000.

Ports éphémères

Chaque connexion d'une IP source vers une même IP et un même port de broker nécessite un port local unique. La plage par défaut de Linux est 32768–60999, soit environ 28 000 ports — l'endroit classique où les tests plafonnent.

Élargir la plage de ports locaux
sysctl net.ipv4.ip_local_port_range
sudo sysctl -w net.ipv4.ip_local_port_range="1024 65000"

Vous obtenez ainsi environ 64 000 connexions par IP source. Au-delà, il faut davantage d'IP source (ou davantage de ports/IP côté broker, ou davantage de machines de charge).

Suivi des connexions

Si le générateur de charge utilise un pare-feu avec suivi des connexions (courant avec Docker et iptables), la table conntrack peut se remplir. Le journal du noyau affiche alors nf_conntrack: table full, dropping packet.

Inspecter et augmenter les limites conntrack
sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
sudo sysctl -w net.netfilter.nf_conntrack_max=262144

Mener le test avec emqtt-bench

emqtt-bench est conçu pour cela. La commande conn ouvre des connexions à un rythme fixe et les maintient ouvertes :

10 000 connexions à 500 par seconde
docker run --rm -it --ulimit nofile=200000:200000 emqx/emqtt-bench conn \
  -h "$MQTT_HOST" -p 1883 \
  -c 10000 -R 500 -k 60 \
  -u "$MQTT_USER" -P "$MQTT_PASS"

Définissez MQTT_HOST avec votre propre broker ; l'endpoint partagé broker.connectmq.com ne sert qu'à de petits smoke tests. -c est le nombre de clients, -R le débit de connexion par seconde et -k le keep-alive en secondes. Utilisez le même keep-alive que vos appareils : un keep-alive plus court signifie davantage de trafic PINGREQ par client inactif.

Pour dépasser une IP source, ajoutez des adresses secondaires à la machine de charge et laissez emqtt-bench répartir les clients avec --ifaddr. Utilisez le réseau de l'hôte pour que le conteneur voie ces adresses :

50 000 connexions depuis deux IP source
docker run --rm -it --network host --ulimit nofile=200000:200000 \
  emqx/emqtt-bench conn \
  -h "$MQTT_HOST" -p 1883 \
  -c 50000 -R 1000 -k 60 \
  --ifaddr 10.0.0.11,10.0.0.12

Montez par paliers — par exemple 10k, 25k, 50k — et maintenez chaque palier plusieurs minutes. Un broker qui accepte 50 000 connexions mais commence à les perdre au cycle de keep-alive suivant n'a pas réussi le test.

Un script Node.js à adapter

Quand il vous faut de vrais flux d'authentification, des abonnements par client ou des client IDs personnalisés, un petit script MQTT.js 5 s'adapte plus facilement qu'un outil de benchmark :

conn-stress.mjs
import mqtt from "mqtt";

const URL = process.env.MQTT_URL ?? "mqtt://broker.connectmq.com:1883";
const TOTAL = Number(process.argv[2] ?? 100); // par défaut : un petit smoke test
const RATE = Number(process.argv[3] ?? 200); // nouvelles connexions par seconde
let opened = 0, up = 0, errors = 0, closes = 0;

function open(i) {
  const c = mqtt.connect(URL, {
    clientId: `stress-${process.pid}-${i}`,
    username: process.env.MQTT_USER,
    password: process.env.MQTT_PASS,
    keepalive: 60,
    connectTimeout: 15_000,
    reconnectPeriod: 2000 + Math.floor(Math.random() * 8000), // jitter par client
  });
  let online = false;
  c.on("connect", () => {
    if (!online) { online = true; up++; }
    c.subscribe(`testmqtt/stress/${i}/cmd`, { qos: 1 });
  });
  c.on("close", () => { if (online) { online = false; up--; } closes++; });
  c.on("error", () => errors++);
}

const timer = setInterval(() => {
  for (let k = 0; k < RATE / 10 && opened < TOTAL; k++) open(opened++);
  if (opened >= TOTAL) clearInterval(timer);
}, 100);

setInterval(() => {
  console.log(`${new Date().toISOString()} opened=${opened} connected=${up} closes=${closes} errors=${errors}`);
}, 1000);
Lancer avec une limite de fichiers relevée
npm install mqtt@5
ulimit -n 100000
MQTT_URL="mqtts://$MQTT_HOST:8883" node conn-stress.mjs 20000 300

Un processus Node.js maintient sans peine plusieurs milliers de connexions inactives ; au-delà, lancez plusieurs processus ou machines. Surveillez le CPU du générateur de charge : s'il est saturé, vos chiffres mesurent le client, pas le broker. Pour interpréter la réponse du broker aux connexions refusées, cherchez le code CONNACK dans la référence des reason codes.

Surveiller le côté broker

Les compteurs côté client ne racontent que la moitié de l'histoire. À chaque palier, relevez sur l'hôte du broker :

Hôte du broker
ss -s                                            # résumé des sockets
ss -tn state established '( sport = :1883 )' | wc -l
ps -o rss=,pcpu= -p "$(pgrep -d, mosquitto)"      # mémoire (Ko) et CPU du processus broker

Mosquitto publie aussi $SYS/broker/clients/connected ; abonnez-vous avec le client en ligne ou avec mosquitto_sub -t '$SYS/broker/clients/connected' -v — voir les topics $SYS MQTT. N'oubliez pas que l'hôte du broker a besoin de la même marge en descripteurs de fichiers : pour Mosquitto sous systemd, définissez LimitNOFILE= dans un drop-in du service et vérifiez max_connections dans mosquitto.conf (le générateur de configuration Mosquitto fournit une base). Les erreurs visibles uniquement dans les journaux du broker sont traitées dans trop de connexions.

La mémoire par connexion se calcule facilement : (RSS à N connexions − RSS au repos) ÷ N. Mesurez-la avec et sans TLS et avec votre nombre réel d'abonnements, car les deux modifient le résultat.

Simuler une tempête de reconnexions

Maintenir 50 000 clients inactifs, c'est la partie facile. Le moment dangereux survient quand tous se reconnectent en même temps — après un redémarrage du broker, un basculement du load balancer ou une micro-coupure réseau régionale. C'est un test simultané du débit de connexion, de la capacité de handshakes TLS et de celle du backend d'authentification.

Deux façons de la déclencher sur votre propre broker :

  1. Redémarrez le broker pendant que le script maintient les connexions, et regardez connected= remonter.
  2. Coupez le réseau entre le générateur de charge et le broker plus longtemps que le keep-alive (par exemple avec une règle iptables ou avec Toxiproxy), puis rétablissez-le.

Notez le temps nécessaire pour revenir au nombre total de connexions et combien de tentatives ont échoué en route. Avec un intervalle de reconnexion fixe, tous les clients réessaient au même instant et le broker reçoit des vagues synchronisées. Le reconnectPeriod avec jitter du script ci-dessus les étale ; comparez les deux variantes et vous verrez pourquoi les tempêtes de reconnexions relèvent autant de la conception des clients que de la capacité du broker.

Checklist du test de stress des connexions

  1. Relevez ulimit -n, élargissez ip_local_port_range et vérifiez conntrack sur chaque machine de charge.
  2. Prévoyez les IP source : environ 64 000 connexions par IP source et par endpoint du broker.
  3. Reproduisez la production : TLS, keep-alive, authentification et abonnements par client.
  4. Montez par paliers et maintenez chacun pendant plusieurs intervalles de keep-alive.
  5. Relevez connexions, mémoire, CPU et latence de connexion du broker à chaque palier.
  6. Déclenchez une tempête de reconnexions et mesurez le temps de récupération.
  7. Recommencez après chaque mise à jour ou changement de configuration du broker.

Un test de stress des connexions est avant tout une affaire d'honnêteté : levez les limites de votre côté, reproduisez le vrai comportement des clients et poussez jusqu'à ce que ce soit le broker — et non le générateur de charge — qui vous montre où se trouve le plafond. Planifiez ensuite votre flotte bien en dessous, et assurez-vous que vos clients se reconnectent avec du jitter pour que ce plafond ne soit jamais testé par accident.

Questions fréquentes

Pourquoi mon test de connexions MQTT plafonne-t-il vers 28 000 clients ?

C'est généralement le générateur de charge qui manque de ports éphémères : par défaut, Linux utilise environ 28 000 ports locaux pour les connexions d'une IP source vers une même IP et un même port de broker. Élargissez net.ipv4.ip_local_port_range ou ajoutez des IP source.

Combien de connexions un broker MQTT peut-il gérer ?

Cela dépend du broker, de la mémoire, du CPU, de TLS, du keep-alive et du trafic de messages : il n'existe pas de chiffre universel. Mesurez-le en augmentant les connexions tout en relevant la mémoire et le CPU du broker, jusqu'au point où la latence de connexion ou les erreurs augmentent.

Peut-on faire un test de stress sur un broker MQTT public ?

Non. Ouvrir des milliers de connexions vers un broker public partagé le dégrade pour tout le monde et vous expose à une limitation ou à un blocage. Ne faites de tests de stress de connexions que sur vos propres brokers ou sur des instances prévues pour cela.