Les messages hors ligne MQTT sont les publications qu'un broker conserve pour un abonné temporairement déconnecté et qu'il lui livre à son retour. Cet article explique précisément quels messages sont concernés, quelles limites s'appliquent sur les brokers réels, et vous guide dans un test reproductible pour observer vous-même la mise en file et la livraison.
Les quatre conditions des messages hors ligne MQTT
La mise en file n'est pas automatique. Un broker ne met un message en file pour un client hors ligne que si toutes les conditions suivantes sont réunies :
| Condition | Ce qu'il faut vérifier |
|---|---|
| Le client dispose d'une session persistante | MQTT 3.1.1 : cleanSession = false. MQTT 5 : Clean Start = false et Session Expiry Interval > 0 |
| La session n'a pas expiré | Expiration MQTT 5 pas encore écoulée, limites du broker non dépassées |
| Le client était déjà abonné à un filtre de topic correspondant | L'abonnement doit exister avant le passage hors ligne |
| La QoS effective est 1 ou 2 | La QoS de livraison est la plus faible entre la QoS de publication et celle de l'abonnement |
Si l'une de ces conditions n'est pas remplie, le message n'est tout simplement pas livré à ce client. L'éditeur ne reçoit aucune erreur, car MQTT découple par conception les éditeurs des abonnés. C'est pourquoi les bugs du type « mon appareil a manqué une commande » sont si courants : l'éditeur reçoit un PUBACK valide du broker, mais le broker n'avait aucune raison de conserver le message pour cet abonné en particulier.
Et la QoS 0 ?
La QoS 0 signifie « au plus une fois ». La spécification autorise un broker à mettre en file des messages QoS 0 pour les sessions hors ligne, mais ne l'impose pas, et la plupart des brokers ne le font pas. Considérez la QoS 0 comme réservée au temps réel. Les différences entre niveaux sont détaillées dans les niveaux de QoS MQTT expliqués.
Et les retained messages ?
Les retained messages relèvent d'un mécanisme distinct. Le broker conserve le dernier retained message de chaque topic et l'envoie à quiconque s'y abonne, avec ou sans session. Ils répondent à la question « quelle est la valeur actuelle ? », pas à « qu'est-ce que j'ai manqué ? ». Un client qui se reconnecte avec une session propre reçoit toujours la dernière valeur retenue lors de son abonnement, mais aucune des mises à jour intermédiaires. Une bonne conception utilise souvent les deux : des retained messages pour l'état, des messages QoS 1 mis en file pour les événements et les commandes.
Les limites des files hors ligne
Chaque broker borne ses files d'attente, faute de quoi un client déconnecté qui ne revient jamais consommerait de la mémoire indéfiniment.
- Durée de vie de la session. En MQTT 5, le session expiry interval détermine combien de temps la file survit. À son expiration, les messages en attente sont supprimés. Sur le serveur MQTT public gratuit broker.connectmq.com, réservé aux tests et aux prototypes, l'expiration de session est plafonnée à 30 minutes. Le choix des valeurs est abordé dans le Session Expiry Interval MQTT.
- Profondeur de file. Les brokers plafonnent le nombre de messages ou d'octets en file par session. Lorsque le plafond est atteint, les nouveaux messages sont rejetés ou les plus anciens évincés, selon le broker.
- Expiration des messages. En MQTT 5, un éditeur peut définir un Message Expiry Interval. Si un message reste en file plus longtemps, le broker le supprime au lieu de livrer des données périmées.
- Taille des messages. Les limites de taille des offres et des brokers s'appliquent aussi aux messages en file (64 Ko sur le serveur MQTT public gratuit, 256 Ko sur l'offre ConnectMQ Serverless Free, 1 Mo sur l'offre Serverless payante).
- Redémarrages du broker. L'emplacement des files dépend du broker. ConnectMQ conserve les sessions et leurs files en mémoire : elles survivent aux déconnexions des clients pendant la durée de session accordée, mais ne sont pas conservées après un redémarrage du broker. Les clients doivent détecter la perte de session (Session Present = 0 dans le CONNACK), se réabonner et s'appuyer sur les retained messages ou sur une resynchronisation applicative pour l'état.
Test reproductible : observer la livraison hors ligne
Ce test prend environ cinq minutes avec les outils en ligne de commande mosquitto. Remplacez q5t9 par votre propre chaîne aléatoire et gardez des payloads inoffensifs : l'espace public est lisible par tous.
- Créez la session. Connectez un abonné avec un ID fixe, Clean Start à false, une expiration de session de 15 minutes et la QoS 1 :
mosquitto_sub -h broker.connectmq.com -p 1883 -V mqttv5 \
-i offline-sub-q5t9 -c -x 900 -q 1 -v \
-t 'connectmq/demo/q5t9/cmd/#'
Passez hors ligne. Appuyez sur Ctrl+C.
Publiez avec différents niveaux de QoS pendant l'absence de l'abonné :
H="-h broker.connectmq.com -p 1883 -V mqttv5 -i offline-pub-q5t9"
mosquitto_pub $H -q 0 -t 'connectmq/demo/q5t9/cmd/a' -m 'qos0 message'
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/cmd/b' -m 'qos1 message'
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/cmd/c' -m 'short-lived' \
-D publish message-expiry-interval 5
mosquitto_pub $H -q 1 -t 'connectmq/demo/q5t9/other' -m 'not subscribed'
- Attendez au moins 10 secondes, puis relancez la commande de l'étape 1.
Vous ne devriez voir que connectmq/demo/q5t9/cmd/b qos1 message. Voici pourquoi les autres manquent :
cmd/aétait en QoS 0 : il n'a pas été mis en file.cmd/cavait une expiration de message de 5 secondes et a été supprimé dans la file.otherne correspondait pas au filtre d'abonnement.
- Recommencez sans
-cà l'étape 1. Rien n'est livré à la reconnexion, car la session a été supprimée à la fermeture de la connexion.
Le même modèle en Python
Un appareil qui se reconnecte et traite tout ce qu'il a manqué :
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes
TOPIC = "connectmq/demo/q5t9/cmd/#"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("Connect failed:", reason_code)
return
print("Connected. Session present:", flags.session_present)
if not flags.session_present:
# Session nouvelle ou perdue : s'abonner et récupérer l'état autrement
client.subscribe(TOPIC, qos=1)
def on_message(client, userdata, msg):
print(f"Processing {msg.topic}: {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="offline-py-q5t9",
protocol=mqtt.MQTTv5)
client.on_connect = on_connect
client.on_message = on_message
client.reconnect_delay_set(min_delay=1, max_delay=60)
props = Properties(PacketTypes.CONNECT)
props.SessionExpiryInterval = 900
client.connect("broker.connectmq.com", 1883, keepalive=60,
clean_start=False, properties=props)
client.loop_forever()
Concevoir en tenant compte des clients hors ligne
La mise en file aide à combler de courtes interruptions, mais elle ne doit pas être votre seul outil de fiabilité. Une conception robuste combine plusieurs mécanismes :
- Utilisez la QoS 1 pour les commandes et les événements, et la QoS 0 pour la télémétrie à haute fréquence où chaque mesure remplace la précédente.
- Gardez une expiration de session réaliste. Assez longue pour couvrir les pannes habituelles, assez courte pour que les appareils morts n'accumulent pas de files.
- Définissez une expiration de message sur les commandes sensibles au temps, pour qu'un appareil qui revient au bout d'une heure n'exécute pas une instruction obsolète.
- Publiez l'état sous forme de retained messages, pour que tout client puisse se resynchroniser instantanément sans dépendre d'une file.
- Rendez vos handlers idempotents. La QoS 1 peut livrer des doublons après une reconnexion ; incluez un identifiant de message ou un numéro de séquence dans le payload.
- Traitez Session Present = 0 comme un événement normal, pas comme une erreur.
- Attendez-vous à une rafale à la reconnexion. Les clients qui étaient hors ligne peuvent recevoir de nombreux messages d'un coup ; le Receive Maximum de MQTT 5 limite le nombre de messages non acquittés reçus en parallèle.
Pour en savoir plus sur les sessions elles-mêmes, lisez les sessions persistantes MQTT et le flag clean session. Si vous débutez, le tutoriel MQTT couvre d'abord les bases du publish/subscribe.
Dépanner les messages hors ligne manquants
- Rien n'arrive après la reconnexion : vérifiez que le client ID est identique, que Clean Start est à false et, en MQTT 5, que l'expiration de session n'est pas nulle.
- Certains messages arrivent, d'autres non : comparez la QoS de chaque publication et celle de l'abonnement, et vérifiez l'expiration des messages.
- Les messages arrivent en double : c'est attendu en QoS 1 après un acquittement interrompu ; dédupliquez dans l'application.
- Session Present vaut 0 de manière inattendue : la session a expiré, le client ID a changé ou le broker a redémarré.
Mettre en file les messages hors ligne avec ConnectMQ
ConnectMQ est un broker MQTT managé qui prend en charge les sessions persistantes, l'expiration de session et l'expiration des messages pour les clients MQTT 5.0 comme MQTT 3.1.1. L'offre Serverless Free prend en charge la QoS jusqu'au niveau 1, ce qui suffit à la mise en file hors ligne, et la QoS 2 est disponible sur les offres payantes. Créez votre broker gratuit sans carte bancaire, connectez-vous avec YOUR_USERNAME, YOUR_PASSWORD et YOUR_CLIENT_ID, et lancez le test ci-dessus sur votre propre endpoint privé. Si vous comparez des fournisseurs, découvrez les critères à examiner pour choisir un broker MQTT cloud.
Questions fréquentes
MQTT stocke-t-il les messages lorsque l'abonné est hors ligne ?
Uniquement si l'abonné dispose d'une session persistante, était déjà abonné à un topic correspondant et que la QoS effective est 1 ou 2. Sinon, le message n'est pas conservé pour ce client.
Pourquoi mes messages QoS 0 sont-ils perdus pendant que l'appareil est hors ligne ?
Les messages QoS 0 ne sont normalement pas mis en file pour les sessions hors ligne ; la spécification rend cela facultatif. Publiez et abonnez-vous en QoS 1 si le message doit survivre à une déconnexion.
Combien de temps un broker conserve-t-il les messages hors ligne ?
Jusqu'à l'expiration de la session, l'atteinte d'une limite de file, l'expiration propre du message ou le redémarrage du broker si les sessions sont conservées en mémoire. Sur ConnectMQ, les sessions vivent en mémoire, et le serveur MQTT public gratuit plafonne l'expiration de session à 30 minutes.
Les retained messages remplacent-ils la mise en file hors ligne ?
Non. Un retained message donne à un nouvel abonné la dernière valeur d'un topic, tandis que la mise en file hors ligne livre chaque message QoS 1 ou 2 que le client a manqué. La plupart des systèmes utilisent les deux.



