Tests de brokers

Tester les gros payloads MQTT et la taille maximale des messages

Quelle est la taille maximale d'un message MQTT ? La limite de 256 Mo, les limites du broker (message_size_limit), Maximum Packet Size MQTT 5 et comment tester.

Mis à jour · 9 min de lecture

Tester les gros payloads MQTT et la taille maximale des messages

La taille maximale d'un message MQTT selon le protocole est de 268 435 455 octets, soit environ 256 Mo. En pratique, vous n'irez jamais aussi loin : les brokers, les services cloud et les bibliothèques clientes fixent des limites plus basses, et certains suppriment les messages trop gros sans vous prévenir. Ce guide explique où se trouve chaque limite, comment MQTT 5 la rend visible et comment tester le plafond réel de votre broker avant qu'une image de firmware ou une capture de caméra ne le découvre à votre place.

D'où vient la limite de 256 Mo

Chaque paquet de contrôle MQTT commence par un en-tête fixe contenant le Remaining Length : le nombre d'octets qui suivent. Il est encodé comme un entier de longueur variable sur quatre octets maximum, avec sept bits utiles chacun ; la valeur la plus élevée est donc 2^28 − 1 = 268 435 455.

Ce nombre couvre tout le reste du paquet, pas seulement vos données. Un PUBLISH contient aussi :

  • le nom du topic (longueur sur 2 octets + octets UTF-8),
  • un packet identifier de 2 octets en QoS 1 et 2,
  • en MQTT 5, un bloc de propriétés (content type, user properties, message expiry…).

Le plus gros payload possible est donc légèrement inférieur à 256 Mo, et il diminue avec des topics longs et de nombreuses user properties. Le calculateur de taille de paquet MQTT affiche l'overhead exact pour un topic, une QoS et un payload donnés.

Les limites que vous rencontrez réellement

CoucheOù la limite est définieComportement typique en cas de dépassement
ProtocoleRemaining Length (4 octets)Le paquet ne peut même pas être encodé
Mosquittomax_packet_size, anciennement message_size_limitMQTT 5 : déconnexion avec 0x95 ; 3.1.1 : message supprimé ou connexion fermée
EMQXmqtt.max_packet_size (1 Mo par défaut)Connexion fermée, erreur dans le log du broker
HiveMQmax-packet-size dans la configuration MQTTLes clients MQTT 5 reçoivent 0x95 ; les clients 3.1.1 sont déconnectés
Services IoT cloudQuotas du serviceAWS IoT Core limite les payloads à 128 Ko, Azure IoT Hub les messages device-to-cloud à 256 Ko ; vérifiez les pages de quotas actuelles
Bibliothèque clienteBuffers internes, Maximum Packet Size MQTT 5Erreur à la publication ou suppression silencieuse à la réception
Chemin réseauProxys, load balancers, passerelles WebSocketTimeouts ou resets sur les liens lents

Les valeurs par défaut changent d'une version à l'autre : utilisez ce tableau comme une carte de l'endroit où chercher, pas comme une liste de chiffres garantis. Lisez la référence de configuration de la version exacte que vous exécutez.

Configuration de Mosquitto

Mosquitto 2.x propose deux options liées :

mosquitto.conf
# Maximum size of any MQTT packet the broker accepts, in bytes.
# MQTT 5 clients are told this value in CONNACK.
max_packet_size 1048576

# Older option: maximum PUBLISH payload size. Deprecated in favour of
# max_packet_size, but still found in many existing configs.
# message_size_limit 1048576

Si aucune n'est définie, Mosquitto n'impose pas de limite inférieure à celle du protocole : un seul client peut donc amener le broker à allouer des centaines de mégaoctets. Fixer une limite explicite fait partie d'une configuration de production raisonnable ; le générateur de mosquitto.conf l'inclut.

Maximum Packet Size en MQTT 5

MQTT 3.1.1 ne permet pas de négocier la taille. MQTT 5 ajoute la propriété Maximum Packet Size dans les deux sens :

  • CONNECT → broker : le client annonce le plus gros paquet qu'il accepte de recevoir. Le broker ne doit rien envoyer de plus gros ; si un message correspondant est trop gros pour cet abonné, le broker le supprime pour ce client uniquement.
  • CONNACK → client : le broker annonce le plus gros paquet qu'il accepte. Une bibliothèque cliente correcte refuse d'envoyer des paquets plus gros. Si un tel paquet est malgré tout envoyé, le broker déconnecte avec le reason code 0x95 Packet too large.

Ce second cas est l'erreur que l'on recherche. Sa signification et les codes associés figurent dans la référence des reason codes MQTT et sont détaillés dans MQTT packet too large.

Test rapide avec mosquitto_pub

Générez des fichiers de tailles connues et faites-les passer par le broker. -f envoie un fichier comme payload ; -N empêche mosquitto_sub d'ajouter un retour à la ligne, pour que le nombre d'octets reste exact.

large-payload-cli.sh
HOST=broker.connectmq.com
TOPIC="testmqtt/payload-$RANDOM/blob"

for size in 64K 512K 1M 4M; do
  head -c "$size" /dev/urandom > "/tmp/blob-$size"
done

# Terminal 1 : recevoir un message et compter ses octets
mosquitto_sub -h "$HOST" -t "$TOPIC" -V mqttv5 -C 1 -W 30 -N | wc -c

# Terminal 2 : envoyer une taille à la fois
mosquitto_pub -h "$HOST" -t "$TOPIC" -V mqttv5 -q 1 -f /tmp/blob-1M -d

-d affiche chaque paquet envoyé et reçu : vous voyez si le PUBLISH a quitté le client et si la connexion a été fermée juste après. Si wc -c affiche la taille du fichier, le message est arrivé ; si mosquitto_sub expire au bout de 30 secondes, il n'est pas arrivé. Pour simuler un abonné limité, ajoutez -D connect maximum-packet-size 65536 à mosquitto_sub : les messages plus gros disparaissent alors pour ce seul client.

Balayage automatique des tailles en Python

Pour une réponse reproductible, ce script paho-mqtt 2.x publie des payloads de taille croissante en QoS 1, vérifie chacun avec un hash SHA-256 et s'arrête au premier échec. Il affiche aussi la limite annoncée par le broker.

payload_sweep.py
import hashlib, os, threading, time, uuid
import paho.mqtt.client as mqtt

HOST, PORT = "broker.connectmq.com", 1883
TOPIC = f"testmqtt/payload-{uuid.uuid4().hex[:8]}/blob"
SIZES = [1_024, 65_536, 262_144, 1_048_576, 4_194_304, 16_777_216]
TIMEOUT = 30
result, got = {}, threading.Event()

def on_connect(client, userdata, flags, reason_code, properties):
    limit = getattr(properties, "MaximumPacketSize", None)
    print(f"connected: {reason_code}, broker max packet = {limit or 'not advertised'}")

def on_disconnect(client, userdata, flags, reason_code, properties):
    print(f"disconnected: {reason_code}")

def on_message(client, userdata, msg):
    result["digest"] = hashlib.sha256(msg.payload).hexdigest()
    got.set()

def make_client(name):
    c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                    client_id=f"{name}-{uuid.uuid4().hex[:6]}", protocol=mqtt.MQTTv5)
    # c.username_pw_set(os.environ["MQTT_USER"], os.environ["MQTT_PASS"])
    c.on_connect, c.on_disconnect, c.on_message = on_connect, on_disconnect, on_message
    c.connect(HOST, PORT)
    c.loop_start()
    return c

sub = make_client("sweep-sub")
sub.subscribe(TOPIC, qos=1)
pub = make_client("sweep-pub")
time.sleep(2)

for size in SIZES:
    payload = os.urandom(size)
    got.clear()
    start = time.perf_counter()
    info = pub.publish(TOPIC, payload, qos=1)
    if info.rc != mqtt.MQTT_ERR_SUCCESS:
        print(f"{size:>10} B  FAIL  publish rc={info.rc}")
        break
    if not got.wait(TIMEOUT):
        print(f"{size:>10} B  FAIL  not delivered within {TIMEOUT}s")
        break
    ok = result["digest"] == hashlib.sha256(payload).hexdigest()
    print(f"{size:>10} B  {'OK  ' if ok else 'BAD '}  {(time.perf_counter() - start) * 1000:.0f} ms")

pub.loop_stop(); sub.loop_stop()

Sorties typiques et leur signification :

  • Toutes les tailles OK : le broker accepte au moins 16 Mo. Demandez-vous si c'est vraiment ce que vous voulez.
  • Livré jusqu'à N, puis « not delivered », sans déconnexion : le broker supprime silencieusement les messages trop gros, ou la limite de l'abonné lui-même s'est appliquée.
  • « disconnected: Packet too large » : un broker MQTT 5 applique max_packet_size. La dernière taille passée est votre plafond pratique.
  • La latence augmente fortement avec la taille : vous mesurez la bande passante, pas le broker. Comparez les chiffres avec votre débit montant.

Lancez le balayage une fois en TCP et une fois en WebSockets (port 8083 ou 8084 avec le chemin /mqtt) : les proxys placés devant les listeners WebSocket ont parfois leurs propres limites de trame ou de buffer. Vous pouvez aussi essayer des payloads individuels de façon interactive dans le client MQTT en ligne.

Les gros payloads ont des effets secondaires

Même quand le broker accepte un message de 10 Mo, il n'est pas gratuit :

  • Blocage en tête de file. MQTT fonctionne sur une seule connexion TCP. Pendant la transmission d'un gros PUBLISH, plus rien ne passe sur cette connexion, y compris les petites commandes urgentes et les paquets PINGREQ. Sur un lien cellulaire lent, cela peut déclencher des timeouts de keep-alive.
  • Mémoire. Le broker conserve généralement le message entier en mémoire, une fois par abonné en file d'attente avec session persistante hors ligne. Les gros messages retenus y restent indéfiniment.
  • Renvois. En QoS 1 ou 2, une connexion coupée implique de renvoyer tout le paquet : il n'y a pas de reprise.
  • Fan-out. Un message de 5 Mo vers 200 abonnés représente 1 Go de trafic sortant. Voir l'overhead de bande passante MQTT pour le calcul.

Patterns de conception pour les gros volumes

  1. Gardez les messages petits par défaut. Télémétrie, événements et commandes devraient tenir en octets ou en kilo-octets. Les encodages binaires comme CBOR ou Protobuf réduisent nettement les payloads JSON.
  2. Découpez les gros fichiers. Divisez-les en morceaux numérotés (par exemple de 64 Ko) sur …/file/<id>/<n>, envoyez un manifeste avec la taille totale et le hash, puis réassemblez côté récepteur. Transfert de fichiers via MQTT détaille une implémentation complète.
  3. Envoyez un pointeur, pas le fichier. Téléversez le fichier vers un stockage objet en HTTP et publiez l'URL avec ses métadonnées via MQTT. C'est le choix habituel pour les images de caméra et le firmware.
  4. Fixez une limite explicite sur le broker, un peu au-dessus de votre plus gros message légitime, pour qu'un client défaillant ne puisse pas épuiser la mémoire.

Publier un payload de test dans le navigateur

Le plafond de 256 Mo du protocole est un chiffre théorique ; la limite qui compte est la plus petite de tout votre chemin, du buffer du client à la configuration du broker et au quota du cloud. Mesurez-la avec un balayage de tailles, rendez-la explicite dans la configuration du broker et concevez tout ce qui est plus gros sous forme de morceaux ou de pointeurs plutôt que d'un seul message.

Questions fréquentes

Quelle est la taille maximale d'un message MQTT ?

Le protocole autorise des paquets de contrôle jusqu'à 268 435 455 octets (environ 256 Mo), car le champ Remaining Length fait au plus quatre octets. Le payload utile est un peu plus petit, et presque tous les brokers et services cloud imposent une limite bien plus basse.

Pourquoi mon gros message MQTT disparaît-il sans erreur ?

En MQTT 3.1.1, un broker ne peut pas refuser un PUBLISH précis en indiquant une raison : beaucoup de brokers suppriment donc silencieusement les messages trop gros ou ferment la connexion. Utilisez MQTT 5, lisez le Maximum Packet Size du CONNACK et surveillez le reason code 0x95 Packet too large.

Faut-il envoyer des fichiers ou des images via MQTT ?

Les petits fichiers de quelques centaines de kilo-octets passent sans problème sur la plupart des brokers. Pour des fichiers plus gros, découpez-les en morceaux numérotés ou publiez une URL vers un stockage objet, afin qu'un message énorme ne bloque pas la connexion et n'épuise pas la mémoire du broker.