Aller au contenu

MQTT

Le protocole MQTT (Message Queuing Telemetry Transport) est un protocole de type publish/subscribe développé en 1999 par Andy Stanford-Clark (IBM) et Arlen Nipper (Arcom, Eurotech et Cirrus Link). À l’origine, ce protocole servait à surveiller un oléoduc dans le désert et comme la liaison satellite qu’ils utilisaient à l’époque était très chère, le protocole devait être efficace en bande passante.

Les Brokers

Pour que des systèmes puissent communiquer entre eux, MQTT a besoin d’un broker. Le broker joue le rôle de “serveur” et c’est lui qui reçoit et distribue les messages conformément aux abonnements (subscriptions) des clients.

Plusieurs brokers sont librement disponibles et vous n’avez pas besoin d’écrire le vôtre pour utiliser MQTT. Les plus populaires d’entre eux sont :

Ces brokers sont aussi disponible sous la forme d’une image Docker facile à mettre en œuvre.

Si c’est juste pour tester, vous pouvez aussi utiliser des brokers dans le cloud :

Les Clients

Les clients sont les participants aux échanges. Un client peut être un publisher (éditeur), un subscriber (abonné), ou les deux à la fois.

Chaque client doit avoir un identificateur unique appelé Client ID. Il est important que chaque client puisse être identifié de manière univoque grâce à cet ID. Si un deuxième client venait à se connecter au broker avec le même ID, alors il “volerait” la session du premier.

Une possibilité pour garantir l’unicité des ID consiste à utiliser l’adresse MAC de la carte réseau, ou tout autre identifiant unique déjà disponible sur le système.

Attention

Notez qu’avec cette pratique, si vous devez remplacer un appareil défectueux, vous changerez son identifiant et vous risquez alors de devoir reconfigurer votre système!

Les Topics

Les topics permettent d’organiser les messages dans MQTT. Lorsqu’un client publie un message, il l’associe à un topic. C’est un peu comme une clé ou un URI qui donne une identité au message.

Les topics sont des chaînes de caractères codés en UTF8 et qui sont structurés en plusieurs niveaux séparés par des barres obliques /. Un topic ressemble donc beaucoup à un path pour un fichier. C’est une structure hiérarchique qui n’a pas forcément une racine commune.

Voici quelques exemples de topics :

  • appartement/rez-de-chaussée/salon/température
  • CH/Fribourg/Pérolles/80
  • 44EE0753-0CFD-4F5C-970D-DE8D31738FA6/etat

Notez que les topics sont sensibles aux majuscules et minuscules et qu’ils ne peuvent pas être vides. Un topic ne peut pas non plus commencer par un dollar ($) car ces topics sont réservés pour le système lui-même.

Lorsqu’un client publie un message, il utilise toujours un topic complet, mais lorsqu’il s’abonne à certains topics, il peut utiliser des caractères “jokers” (wildcard character). MQTT permet deux types de jokers :

  • Un plus (+) permet de laisser passer n’importe quoi sur un niveau donné. Par exemple appartement/rez-de-chaussée/+/température recevra toutes les températures de toutes les chambres du rez-de-chaussée.
  • Un dièse (#) permet de laisser passer n’importe quoi sur plusieurs niveaux. Il ne peut y avoir qu’un seul # dans un topic et ça doit être le dernier caractère. Par exemple appartement/# s’abonne à tous les messages descendants de l’appartement`.

Les bonnes pratiques pour définir les topics sont les suivantes

  • Ne commencez pas le topic par un slash (/). Le premier niveau définit la racine d’une arborescence.
  • N’utilisez que les caractères ASCII simples. Choisissez plutôt des topics en anglais et évitez les caractères accentués, les caractères spéciaux et les espaces.
  • Choisissez des topics courts et concis. En effet, les topics sont transmis dans chaque paquet et des topics longs gaspillent inutilement de la bande passante.
  • Il est parfois utile de connaître l’expéditeur d’un message. Si c’est le cas, placez le client ID dans un niveau du topic.
  • Ne vous abonnez pas à la racine #. La plupart des brokers l’interdisent et vous risquez de recevoir plus de messages que votre système est capable de gérer.

Exercice MQTT/1

Concevez des topics pour les applications suivantes:

  • Station météo connectée
  • Contrôle d’une habitation (domotique)
  • Messagerie de type WhatsApp

Les Messages

Lorsqu’un client publie sur un topic, il publie également du contenu. MQTT ne définit pas la structure de ce contenu et vous pouvez envoyer ce que vous voulez (du texte, du JSON, du Protocol Buffer, des images, …). On dit que MQTT est agnostique en matière de données (data agnostic)

Qualité de Service

La qualité de service (ou QoS pour Quality of Service) garantit la livraison des paquets entre les participants. On pourrait se demander pourquoi nous avons besoin de faire quelque chose de spécial car MQTT est basé sur TCP qui garantit déjà un transport fiable, mais le QoS de MQTT va plus loin. Il garantit la livraison des paquets même si le client subit une déconnexion plus longue (par exemple quand un véhicule traverse un tunnel) ou s’il change de réseau (par exemple de WiFi à LTE)

MQTT définit 3 niveaux de qualité de service

  • Niveau 0 (At most once) : Le client envoie le message, mais le broker ne quittance pas la réception et il n’y a aucune garantie que le broker a bien reçu le message. Vous pouvez utiliser ce niveau si la perte d’un message n’a pas vraiment d’importance pour votre système. Par exemple, pour une station météo qui envoie la température toutes les 10 secondes, on peut sans problème tolérer la perte d’un paquet.
  • Niveau 1 (At least once) : Cette fois le broker quittance la réception du message, mais si cette quittance est perdue, alors le client décide de renvoyer le message une seconde fois. On est donc sûr que le message a bien été reçu par le broker, mais il se peut que les abonnées reçoivent plusieurs fois ce message. Pour la plupart des applications, le niveau 1 est le niveau de QoS recommandé.
  • Niveau 2 (Exactly once) : Dans de rares cas où on doit être sûr que le broker reçoive tous les messages et qu’on ne peut pas tolérer des duplicatas, alors on utilise le niveau 2. Dans ce niveau, les échanges sont plus compliqués, plus lents, et consomment plus de bande passante.

La bonne pratique est d’utiliser le niveau 1 par défaut et de choisir un autre niveau seulement si c’est nécessaire.

Les Messages Conservés

Lorsqu’un client publie un message, ce message est normalement ajouté dans la liste (queue) de chaque client. Un nouveau client qui se connecte au broker et qui s’abonne à un topic commence avec une liste vide et il doit attendre qu’un client publie quelque chose pour recevoir un premier message.

Dans certains cas, ce n’est pas vraiment idéal. Reprenons le cas de notre station météo. Si, pour économiser l’énergie, le système n’envoie la température qu’une fois par minute. Un nouveau client doit attendre en moyenne 30 secondes avant d’afficher quoi que ce soit. Ca serait plus pratique si le broker se souvenait du dernier message reçu et qu’il pouvait directement l’envoyer au nouveau client. MQTT a prévu ce cas, et quand un client publie un message, il peut le marquer comme retained message. Le broker se souvient donc de ce message et peut directement l’envoyer aux nouveaux clients.

Dernière Volonté et Testament

La dernière fonctionnalité pour améliorer la fiabilité d’un système que nous étudions avec MQTT c’est la “dernière volonté”. Lorsqu’un client se connecte au broker, il peut mentionner une dernière volonté sous la forme d’un topic associé à un message. Dès que le broker constate la perte brutale de son client, alors il publie sa dernière volonté.

Cette fonctionnalité permet de diffuser l’information relative à la perte d’un client et permet aux autres participants de prendre les mesures qui s’imposent.

Exercice

Exercice MQTT/2

  • Installez un client MQTT sur votre PC. Je vous recommande MQTT Explorer pour commencer, mais vous pouvez installer des outils en ligne de commande (https://hivemq.github.io/mqtt-cli/, https://mosquitto.org/download/)
  • Par groupe de deux, choisissez un broker de test (par exemple test.mosquitto.org) ainsi qu’un topic (par exemple heiafr/isc-rs-2/group/1/msg)
  • Le premier étudiant se prépare à recevoir les messages (par exemple en s’abonnant à heiafr/isc-rs-2/#)1
  • Le second étudiant publie un message dans le topic choisi

Défi : Envoyer “I Love Embedded Systems” sur le topic heiafr/isc/edu/<student name>/msg en remplaçant <student name> par votre nom

Format des Paquets MQTT

Le format des paquets MQTT est très simple. Il est composé de 3 parties :

MQTT

Format des paquets MQTT
  • Fixed header : Un entête fixe présent dans tous les paquets MQTT
  • Variable header : Un entête “variable” présent dans certains paquets
  • Payload : Le payload présent lui aussi dans certains paquets

Le fixed header est lui aussi composé de trois parties :

MQTT

Format du "fixed header"
  • Le Control Packet Type indique le type de paquet (voir la table ci-dessous)
  • Les Flags sont spécifiques aux Control Packet Type. Par exemple, avec un message de type PUBLISH, les flags permettent de stocker le niveau de QoS ou d’indiquer si le message doit être de type “conservé” (retained).
  • Le Packet Length indique la taille du reste du paquet. Cette taille est codée de manière compacte sur 1, 2, 3 ou 4 bytes. Les petits paquets (moins de 128 Byte) sont codés sur un Byte, et avec 4 Byte, la taille maximum représentable est 268‘435‘455 (256 MByte). Les paquets MQTT sont donc limités à 256 MByte).

Type de paquet :

Name Value Description
Reserved 0 Reserved
CONNECT 1 Client request to connect to Server
CONNACK 2 Connect acknowledgment
PUBLISH 3 Publish message
PUBACK 4 Publish acknowledgment
PUBREC 5 Publish received (assured delivery part 1)
PUBREL 6 Publish release (assured delivery part 2)
PUBCOMP 7 Publish complete (assured delivery part 3)
SUBSCRIBE 8 Client subscribe request
SUBACK 9 Subscribe acknowledgment
UNSUBSCRIBE 10 Unsubscribe request
UNSUBACK 11 Unsubscribe acknowledgment
PINGREQ 12 PING request
PINGRESP 13 PING response
DISCONNECT 14 Client is disconnecting
Reserved 15 Reserved

Le pseudocode suivant est utilisé pour encoder une taille \(X\) (source : Spécification MQTT Version 3.1.1) :

do
    encodedByte = X MOD 128
    X = X DIV 128
    // if there are more data to encode, set the top bit of this byte
    if ( X > 0 )
        encodedByte = encodedByte OR 128
    endif
    'output' encodedByte
while ( X > 0 )

Exercice MQTT/3

Comment est codée une taille de 1700 Byte ?

Solution

a4 0d (\(13 \cdot 128 + 36\))

Exercice MQTT/4

Écrivez le pseudo-code de la méthode qui permet de décoder une taille

Solution
multiplier = 1
value = 0
do
    encodedByte = 'next byte from stream'
    value += (encodedByte AND 127) * multiplier
    if (multiplier > 128*128*128)
        throw Error(Malformed Remaining Length)
    multiplier *= 128
while ((encodedByte AND 128) != 0)

Exercice MQTT/5

Quel est le numéro de port utilisé par défaut par MQTT ?

Solution

1883

Exercice MQTT/6

Démarrez “Wireshark” sur votre PC, capturez les paquets MQTT et publiez un message de plus de 128 Bytes vers un broker. Étudiez la capture regardez comment est codée la taille du paquet.

Faites le codage à la main et vérifiez que vous obtenez la même chose

Solution

if=/dev/urandom bs=1 count=1672 | \
  mosquitto_pub -h test.mosquitto.org \
  -t heiafr/isc-2rs/group/1/msg -s

Alternatives à MQTT

MQTT est souvent un très bon choix pour les systèmes embarqués, mais si vous avez besoin d’autre chose, voici quelques alternatives possibles :

  • CoAP : voir les autres chapitres de ce cours
  • AMQP : moins populaire dans les systèmes embarqués
  • XMPP : basé sur XML, donc assez lourd pour les systèmes embarqués

Pour implémenter le modèle publisher/subscriber on peut aussi utiliser les systèmes suivants :

  • ZeroMQ : Un système performant ne nécessitant pas de broker.
  • Redis : Redis est plutôt un système de stockage de données (en RAM) souvent utilisé pour implémenter un cache, mais il offre également des fonctions de publisher/subscriber.
  • NATS : Un système de messaging écrit en Go avec des librairies clients disponibles pour la plupart des langages.
  • Kafka : Un système plutôt adapté au cloud et aux plus gros systèmes.

  1. Avec MQTT Explorer, lorsque vous configurez la connexion, vous devez cliquer sur “ADVANCED” et configurer l’abonnement (Subscription)