La QoS MQTT (Quality of Service, ou qualité de service) est la garantie de livraison qu'un client demande lorsqu'il publie ou s'abonne. Dans ce guide, vous verrez précisément quels paquets échangent les QoS 0, 1 et 2, pourquoi la QoS qui atteint un abonné peut être inférieure à celle utilisée à la publication, comment les sessions mettent les messages en file d'attente pour les clients hors ligne, et comment tester tout cela en Python sur le serveur MQTT public gratuit broker.connectmq.com.
Si vous découvrez MQTT, commencez par Qu'est-ce qu'un broker MQTT ? puis revenez ici.
Ce que garantit réellement la QoS MQTT
MQTT repose sur TCP : au sein d'une même connexion, les octets ne sont ni perdus ni réordonnés. La QoS concerne autre chose : ce qui se passe lorsqu'une connexion tombe au milieu d'une livraison, et si le destinataire risque de recevoir deux fois le même message.
Deux points sont souvent mal compris :
- La QoS s'applique saut par saut, pas de bout en bout. La QoS du publisher régit le trajet du publisher vers le broker. La QoS de l'abonnement régit le trajet du broker vers l'abonné. Le broker sert d'intermédiaire pour les deux.
- Une QoS plus élevée n'est pas « meilleure » par défaut. Chaque niveau ajoute des paquets, de l'état et de la latence. Le bon choix dépend de ce que vous coûte un message perdu ou dupliqué.
| Niveau | Nom | Garantie | Paquets par message (un trajet) | Doublons possibles ? |
|---|---|---|---|---|
| QoS 0 | At most once (au plus une fois) | Au mieux, sans accusé de réception | 1 | Non, mais une perte est possible |
| QoS 1 | At least once (au moins une fois) | Livré, peut être répété | 2 | Oui |
| QoS 2 | Exactly once (exactement une fois) | Livré une seule fois, sans doublon | 4 | Non |
QoS 0 : au plus une fois
La QoS 0 fonctionne en « fire and forget ». L'émetteur écrit un paquet PUBLISH sur le socket et l'oublie aussitôt. Il n'y a ni identifiant de paquet ni accusé de réception.
Publisher Broker
| |
|------ PUBLISH (QoS 0) ---->|
| | (no reply, nothing stored)
Si la connexion TCP se rompt alors que le paquet est encore dans un buffer du système d'exploitation, le message est perdu et personne n'en sait rien. Cela peut sembler gênant, mais pour une mesure de température envoyée toutes les deux secondes, la mesure suivante remplace de toute façon celle qui a été perdue. La QoS 0 offre la latence la plus faible et le moins de surcharge sur les appareils contraints.
QoS 1 : au moins une fois
En QoS 1, l'émetteur attribue un identifiant de paquet et conserve le message jusqu'à ce que le destinataire réponde par un PUBACK portant le même identifiant.
Publisher Broker
| |
|-- PUBLISH (QoS 1, id=7) -->| broker stores and forwards
|<------- PUBACK (id=7) -----|
| | publisher deletes message 7
Si le PUBACK n'arrive jamais (par exemple parce que la connexion tombe), l'émetteur renvoie le PUBLISH avec l'indicateur DUP après s'être reconnecté. Le piège : le destinataire a peut-être déjà traité la première copie, et il voit alors le message deux fois. C'est pourquoi la QoS 1 signifie au moins une fois.
La QoS 1 est la bête de somme de la plupart des systèmes IoT. Rendez vos consommateurs idempotents (par exemple en incluant un identifiant de message ou un horodatage dans le payload et en ignorant les répétitions), et la QoS 1 suffit généralement.
QoS 2 : exactement une fois
La QoS 2 utilise un échange en quatre étapes afin que les deux parties s'accordent sur le fait que le message a été livré exactement une fois.
Publisher Broker
| |
|--- PUBLISH (QoS 2, id=9) --->| broker stores id 9, does not forward twice
|<------- PUBREC (id=9) -------| "received"
|-------- PUBREL (id=9) ------>| "release it, I will not resend"
|<------- PUBCOMP (id=9) ------| "complete", both sides drop id 9
Le rôle de chaque étape :
- PUBLISH : l'émetteur stocke le message et l'envoie avec un identifiant de paquet.
- PUBREC : le destinataire enregistre l'identifiant. Si un PUBLISH en double portant le même ID arrive, il est reconnu et n'est pas livré une seconde fois.
- PUBREL : l'émetteur supprime le message et ne garde plus en mémoire que l'identifiant.
- PUBCOMP : le destinataire oublie l'identifiant. L'échange est terminé.
Le coût : deux allers-retours par message et un état conservé des deux côtés jusqu'à la fin du flux. Utilisez la QoS 2 lorsqu'un doublon causerait un réel préjudice et que vous ne pouvez pas rendre le consommateur idempotent, par exemple pour une commande qui incrémente un compteur ou déclenche un événement de facturation.
Rétrogradation de la QoS MQTT : pourquoi vous n'obtenez pas toujours la QoS demandée
La QoS reçue par un abonné n'est pas simplement celle utilisée par le publisher. Trois valeurs interagissent.
QoS effective = min(QoS de publication, QoS d'abonnement)
Lorsque le broker transmet un message, il utilise la plus faible entre la QoS du PUBLISH d'origine et la QoS accordée pour l'abonnement.
| QoS du publisher | QoS de l'abonnement | Livré à l'abonné |
|---|---|---|
| 0 | 2 | 0 |
| 1 | 2 | 1 |
| 2 | 1 | 1 |
| 2 | 0 | 0 |
| 2 | 2 | 2 |
Si vous avez besoin d'une livraison exactement une fois de bout en bout, le publisher et chaque abonné doivent donc utiliser la QoS 2. Un seul abonnement en QoS 0 transforme discrètement le flux de cet abonné en « au mieux ».
La Maximum QoS du broker (MQTT 5)
En MQTT 5, le broker peut annoncer une Maximum QoS dans son paquet CONNACK. Si cette propriété est absente, le client peut supposer que la QoS 2 est prise en charge. Si elle est présente, le client ne doit pas publier au-delà de ce niveau, et le broker n'accordera pas d'abonnement à un niveau supérieur (le SUBACK vous indique la QoS accordée).
Sur ConnectMQ, cela dépend de l'endroit où vous vous connectez :
| Environnement | Maximum QoS |
|---|---|
| Zone de test publique de broker.connectmq.com | QoS 2 |
| Offre Serverless (Free) | QoS 1 |
| Offre Serverless (Paid) | QoS 2 |
| Dedicated, BYOC, Self Managed Enterprise | QoS 2 |
Vérifiez toujours la QoS accordée dans le SUBACK au lieu de la supposer. La plupart des bibliothèques clientes l'exposent dans le callback d'abonnement. La spécification MQTT 5 sur docs.oasis-open.org décrit les règles exactes. Pour un tour d'horizon des autres propriétés du CONNACK, consultez Les fonctionnalités de MQTT 5.
Sessions, expiration de session et file d'attente hors ligne
La QoS ne protège les messages lors d'une déconnexion que si la session survit à cette déconnexion. La session est l'état que le broker conserve pour un client : ses abonnements, les messages QoS 1/2 en cours de transmission et les messages mis en file d'attente pendant qu'il est hors ligne.
- MQTT 3.1.1 utilise l'indicateur Clean Session.
cleanSession=truesupprime la session à la déconnexion.cleanSession=falsedemande au broker de la conserver. - MQTT 5 scinde ce rôle en Clean Start (repartir de zéro ou non à la connexion) et Session Expiry Interval (nombre de secondes pendant lesquelles la session survit après la déconnexion).
Tant qu'une session persistante existe et que le client est hors ligne, le broker met en file d'attente les messages QoS 1 et QoS 2 qui correspondent à ses abonnements. Les messages QoS 0 ne sont pas mis en file d'attente pour les clients hors ligne. Lorsque le client se reconnecte avec le même client ID et sans clean start, les messages en attente sont livrés.
Subscriber (id=sensor-reader) Broker
|-- CONNECT clean_start=0, expiry=600 -->|
|-- SUBSCRIBE plant/# QoS 1 ------------>|
| ... connection lost ... |
| | PUBLISH plant/a QoS 1 -> queued
| | PUBLISH plant/b QoS 0 -> dropped
|-- CONNECT clean_start=0 --------------->|
|<-- CONNACK session_present=1 -----------|
|<-- PUBLISH plant/a ---------------------|
Sur ConnectMQ, l'expiration de session est plafonnée selon l'offre, et dans la zone de test publique, les sessions vivent jusqu'à 30 minutes. Notez aussi que les sessions sont conservées en mémoire : elles ne survivent pas à un redémarrage du broker. Concevez donc vos flux critiques pour qu'ils tolèrent une reconnexion avec session_present=0.
Comment choisir le bon niveau de QoS MQTT
| Cas d'usage | QoS recommandée | Pourquoi |
|---|---|---|
| Télémétrie haute fréquence (température, vibrations) | 0 | L'échantillon suivant remplace celui qui est perdu ; surcharge minimale |
| État des appareils, alarmes, événements | 1 | Doit arriver ; les doublons sont faciles à ignorer |
| Commandes d'actionneurs idempotentes (« régler la vanne à 40 % ») | 1 | Répéter la même commande est sans conséquence |
| Commandes non idempotentes (« incrémenter le compteur », « distribuer ») | 2 | Un doublon changerait le résultat |
| Événements de facturation, de comptage ou de transaction | 2 (ou 1 avec des ID de déduplication) | Comptabilisation exactement une fois |
Une règle pratique : utilisez par défaut la QoS 1 avec des consommateurs idempotents, descendez en QoS 0 pour les données à haut débit dont vous pouvez vous permettre de perdre une partie, et réservez la QoS 2 aux quelques types de messages pour lesquels un doublon est un bug.
En pratique : QoS MQTT 1 et 2 en Python
Le script suivant utilise paho-mqtt 2.x et la zone publique de broker.connectmq.com (sans nom d'utilisateur ni mot de passe). Il s'abonne en QoS 2, publie un message en QoS 1 et un autre en QoS 2, et journalise les accusés de réception. Installez la bibliothèque avec pip install "paho-mqtt>=2.0".
import time
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
UID = uuid.uuid4().hex[:8]
TOPIC = f"connectmq/demo/{UID}/qos"
def on_connect(client, userdata, flags, reason_code, properties):
print("CONNACK:", reason_code)
if properties is not None and hasattr(properties, "MaximumQoS"):
print("Broker Maximum QoS:", properties.MaximumQoS)
client.subscribe(f"{TOPIC}/#", qos=2)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
print("SUBACK granted:", [rc.value for rc in reason_code_list])
def on_message(client, userdata, msg):
print(f"Received {msg.topic} QoS={msg.qos} payload={msg.payload.decode()}")
def on_publish(client, userdata, mid, reason_code, properties):
print(f"Publish flow complete for mid={mid}")
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id=f"qos-demo-{UID}",
protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message
client.on_publish = on_publish
client.connect(BROKER, 1883, keepalive=30)
client.loop_start()
time.sleep(2)
client.publish(f"{TOPIC}/one", "hello at least once", qos=1).wait_for_publish()
client.publish(f"{TOPIC}/two", "hello exactly once", qos=2).wait_for_publish()
time.sleep(2)
client.loop_stop()
client.disconnect()
En QoS 1, on_publish se déclenche à l'arrivée du PUBACK. En QoS 2, il ne se déclenche qu'après le PUBCOMP, à la fin de l'échange en quatre étapes. Si aucune ligne Maximum QoS n'est affichée, c'est que le broker n'a pas envoyé la propriété, ce qui signifie que la QoS 2 est autorisée.
Observer les paquets avec la CLI Mosquitto
Les outils en ligne de commande Eclipse Mosquitto affichent chaque paquet avec -d, ce qui rend visibles les flux décrits ci-dessus. Dans un terminal :
mosquitto_sub -h broker.connectmq.com -p 1883 -i qos-sub-a1b2 -t "connectmq/demo/a1b2/#" -q 2 -v -d
Dans un second terminal, publiez en QoS 1 puis en QoS 2 :
mosquitto_pub -h broker.connectmq.com -p 1883 -i qos-pub-a1b2 -t "connectmq/demo/a1b2/q1" -q 1 -m "hello1" -d
mosquitto_pub -h broker.connectmq.com -p 1883 -i qos-pub-a1b2 -t "connectmq/demo/a1b2/q2" -q 2 -m "hello2" -d
La sortie du publisher montre exactement les séquences décrites plus haut :
Client qos-pub-a1b2 sending PUBLISH (d0, q1, r0, m1, 'connectmq/demo/a1b2/q1', ... (6 bytes))
Client qos-pub-a1b2 received PUBACK (Mid: 1, RC:0)
...
Client qos-pub-a1b2 sending PUBLISH (d0, q2, r0, m1, 'connectmq/demo/a1b2/q2', ... (6 bytes))
Client qos-pub-a1b2 received PUBREC (Mid: 1)
Client qos-pub-a1b2 sending PUBREL (m1)
Client qos-pub-a1b2 received PUBCOMP (Mid: 1, RC:0)
Remplacez a1b2 par votre propre chaîne aléatoire. Tout ce qui se trouve dans la zone publique est lisible par n'importe qui : n'y publiez jamais de données sensibles. Pour expérimenter la rétrogradation, abonnez-vous avec -q 0 et publiez avec -q 2 : la sortie de débogage de l'abonné affichera q0 sur le PUBLISH livré. Vous trouverez d'autres astuces dans notre article sur le serveur MQTT public gratuit.
Les erreurs de QoS les plus courantes
- Supposer une QoS 2 de bout en bout alors qu'un abonné utilise la QoS 0. C'est le maillon le plus faible qui l'emporte.
- Utiliser la QoS 2 pour de la télémétrie à haut débit. Quatre paquets par message et par trajet s'accumulent vite, et la fenêtre de messages en transit (Receive Maximum) se remplit.
- Des client IDs aléatoires avec des sessions persistantes. La file d'attente hors ligne ne fonctionne que si le client se reconnecte avec le même client ID. Un nouvel ID aléatoire crée une nouvelle session vide.
- Ignorer la QoS accordée dans le SUBACK. Sur une offre plafonnée à la QoS 1, un abonnement en QoS 2 est accordé en QoS 1.
Démarrer avec ConnectMQ
Le serveur public est idéal pour apprendre la QoS MQTT, mais le trafic de production doit passer par un broker MQTT managé privé avec identifiants. Inscrivez-vous gratuitement sur app.connectmq.com (offre Serverless, sans carte bancaire), créez un broker et utilisez le nom d'utilisateur, le mot de passe et le client ID générés. Passez à Serverless (Paid) ou Dedicated lorsque vous avez besoin de la QoS 2, de davantage de connexions ou de messages plus volumineux.
Questions fréquentes
Quel est le niveau de QoS MQTT par défaut ?
La plupart des bibliothèques clientes utilisent par défaut la QoS 0, aussi bien pour publier que pour s'abonner. Définissez explicitement la QoS dans votre code pour que le comportement soit clair pour quiconque le lit.
La QoS 2 ralentit-elle MQTT ?
Oui. La QoS 2 nécessite quatre paquets et deux allers-retours par message sur chaque trajet, contre deux paquets pour la QoS 1 et un seul pour la QoS 0. Pour la plupart des télémesures, la QoS 0 ou 1 offre un meilleur débit.
Pourquoi mon abonné a-t-il reçu une QoS 1 alors que j'ai publié en QoS 2 ?
La QoS livrée est le minimum entre la QoS de publication et la QoS accordée à l'abonnement. Votre abonnement a été accordé en QoS 1, soit parce que vous l'avez demandé, soit parce que la Maximum QoS du broker est 1, comme sur l'offre ConnectMQ Serverless (Free).
Les messages QoS 0 sont-ils mis en file d'attente pour les clients hors ligne ?
Non. Les brokers ne mettent en file d'attente que les messages QoS 1 et QoS 2 pour les clients disposant d'une session persistante. Les messages QoS 0 destinés à un client hors ligne sont abandonnés.
Puis-je utiliser la QoS 2 sur broker.connectmq.com ?
Oui. La zone publique anonyme prend en charge les QoS 0, 1 et 2. Les brokers privés prennent en charge la QoS 2 à partir de l'offre Serverless (Paid), tandis que l'offre Serverless gratuite est limitée à la QoS 1.



