Tests de brokers

Surveiller un broker MQTT avec les topics $SYS

Surveillez un broker MQTT avec les topics $SYS : ce que publient Mosquitto et EMQX, s'abonner à $SYS/#, convertir les compteurs en débits et les tracer en Python.

Mis à jour · 8 min de lecture

Surveiller un broker MQTT avec les topics $SYS

La plupart des brokers MQTT rendent compte de leur propre état via MQTT. Abonnez-vous à $SYS/# et le broker publie le nombre de clients connectés, le nombre de messages et d'octets reçus et envoyés, le nombre d'abonnements et de messages retenus qu'il conserve, et depuis combien de temps il tourne. C'est donc le moyen le plus rapide de surveiller un broker. Il ne faut ni exporter ni agent, seulement un client MQTT.

Ce guide présente ce que $SYS expose sur les brokers courants, comment s'y abonner correctement, comment transformer des compteurs bruts en débits utiles, et comment les enregistrer et les tracer avec un court script Python.

Que sont les topics $SYS ?

La spécification MQTT réserve les topics commençant par $ à l'usage interne du broker. Les clients ne doivent pas y publier, et les jokers ne les attrapent pas par accident : un abonnement à # ou à +/foo ne reçoit jamais de messages $SYS. Vous devez vous abonner volontairement à $SYS/# (ou à un filtre plus précis).

Le contenu n'est pas standardisé. Mosquitto, EMQX, VerneMQ et les autres publient chacun leur propre arborescence, et certains services managés la masquent entièrement ou la restreignent par ACL. Pour une référence complète de l'arborescence, consultez les topics $SYS MQTT. Cet article explique comment utiliser ces topics pour la surveillance.

Les topics à surveiller

Voici les noms utilisés par Mosquitto (2.x). Les autres brokers publient des valeurs similaires sous d'autres chemins.

TopicTypeCe qu'il indique
$SYS/broker/clients/connectedjaugeClients connectés en ce moment
$SYS/broker/clients/maximumjaugePic de connexions simultanées depuis le démarrage
$SYS/broker/clients/disconnectedjaugeSessions persistantes dont le client est hors ligne
$SYS/broker/messages/receivedcompteurMessages reçus depuis le démarrage (tous types de paquets)
$SYS/broker/messages/sentcompteurMessages envoyés depuis le démarrage
$SYS/broker/publish/messages/droppedcompteurMessages PUBLISH abandonnés (limites de file, clients hors ligne)
$SYS/broker/bytes/received / sentcompteurOctets réseau entrants et sortants
$SYS/broker/load/messages/received/1mindébitMoyenne glissante sur 1, 5 et 15 minutes
$SYS/broker/subscriptions/countjaugeAbonnements actifs
$SYS/broker/retained messages/countjaugeMessages retenus stockés (attention à l'espace dans le topic)
$SYS/broker/uptimetextePar exemple 86400 seconds
$SYS/broker/versiontexteVersion du broker

On trouve ici deux sortes de valeurs. Les jauges (clients connectés, abonnements) se lisent telles quelles. Les compteurs (messages, octets) ne font qu'augmenter : il faut la différence entre deux échantillons pour obtenir un débit. Mosquitto publie aussi des moyennes glissantes toutes prêtes sous $SYS/broker/load/.... Elles sont pratiques pour un coup d'œil rapide, mais vous ne maîtrisez pas la fenêtre.

Sur EMQX, l'arborescence est organisée par nœud : $SYS/brokers/<node>/version, $SYS/brokers/<node>/uptime, plus les sous-arbres stats/... et metrics/... et des événements optionnels de connexion et de déconnexion des clients. L'intervalle de publication se règle avec sys_msg_interval. Pour des dashboards sur EMQX, son endpoint Prometheus intégré est en général une meilleure source. Voir la supervision avec Prometheus et Grafana.

S'abonner à $SYS

Le test le plus rapide se fait avec mosquitto_sub. Mettez le topic entre apostrophes simples, sinon le shell tentera d'interpréter $SYS comme une variable :

Afficher toute l'arborescence $SYS
mosquitto_sub -h broker.connectmq.com -p 1883 \
  -u "$MQTT_USER" -P "$MQTT_PASS" \
  -v -t '$SYS/#'

Pour ne suivre que quelques valeurs, abonnez-vous à plusieurs filtres précis :

Clients et charge de messages uniquement
mosquitto_sub -h broker.connectmq.com -p 1883 \
  -u "$MQTT_USER" -P "$MQTT_PASS" -v \
  -t '$SYS/broker/clients/connected' \
  -t '$SYS/broker/load/messages/+/1min' \
  -t '$SYS/broker/uptime'

Vous pouvez faire la même chose dans le navigateur : ouvrez le client MQTT en ligne, connectez-vous et abonnez-vous à $SYS/#. Si rien n'arrive, vérifiez ces trois points dans l'ordre :

  1. Le broker a désactivé $SYS. Dans Mosquitto, sys_interval 0 le désactive.
  2. Une ACL l'interdit. Beaucoup de configurations de production n'autorisent qu'un compte administrateur à lire $SYS. Les clients MQTT 5 reçoivent un SUBACK avec le reason code 0x87 Not authorized. Décodez-le avec l'outil de reason codes.
  3. Vous n'avez attendu qu'un intervalle. Certaines valeurs n'apparaissent qu'au tick sys_interval suivant.

Régler l'intervalle de mise à jour

Dans Mosquitto, l'intervalle tient en une seule option :

mosquitto.conf
# Publish $SYS updates every 10 seconds (default). 0 disables $SYS.
sys_interval 10

Un intervalle plus court donne des graphes plus fins, mais coûte un peu de CPU et de bande passante pour chaque abonné à $SYS/#. Pour des dashboards, 10 à 30 secondes suffisent largement. Le générateur de mosquitto.conf propose cette option si vous partez de zéro.

Transformer les compteurs en débits

Les compteurs ne sont utiles que sous forme de différences. Si messages/received valait 1 204 500 à 12:00:00 et 1 210 500 à 12:00:10, le broker a reçu (1210500 − 1204500) / 10 = 600 messages par seconde sur cette fenêtre. Deux règles évitent les calculs trompeurs :

  • Utilisez l'heure de réception de l'échantillon, pas l'intervalle nominal. La gigue réseau et la charge du broker décalent les ticks.
  • Gérez les remises à zéro. Quand le broker redémarre, les compteurs repartent de zéro. Si une nouvelle valeur est inférieure à la précédente, ignorez cette différence au lieu de tracer un énorme pic négatif. Une baisse de uptime confirme le redémarrage.

Enregistrer et tracer $SYS avec Python

Ce script (paho-mqtt 2.x) s'abonne à quelques topics $SYS, calcule des débits à partir des compteurs, affiche une ligne par mise à jour et ajoute le tout à un fichier CSV que vous pourrez tracer ensuite :

sys_monitor.py
import csv, os, time
import paho.mqtt.client as mqtt

HOST, PORT = "broker.connectmq.com", 1883
TOPICS = {
    "$SYS/broker/clients/connected": "gauge",
    "$SYS/broker/subscriptions/count": "gauge",
    "$SYS/broker/messages/received": "counter",
    "$SYS/broker/messages/sent": "counter",
    "$SYS/broker/bytes/received": "counter",
    "$SYS/broker/bytes/sent": "counter",
}
last = {}  # topic -> (horodatage, valeur)
out = csv.writer(open("sys_metrics.csv", "a", newline=""))

def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print("échec de connexion :", reason_code)
        return
    client.subscribe([(t, 0) for t in TOPICS])

def on_message(client, userdata, msg):
    now = time.time()
    try:
        value = float(msg.payload.decode().split()[0])
    except ValueError:
        return
    name = msg.topic.removeprefix("$SYS/broker/")
    if TOPICS[msg.topic] == "counter":
        prev = last.get(msg.topic)
        last[msg.topic] = (now, value)
        if prev is None or value < prev[1]:   # premier échantillon ou redémarrage du broker
            return
        value = (value - prev[1]) / (now - prev[0])
        name += "/s"
    out.writerow([int(now), name, round(value, 2)])
    print(f"{time.strftime('%H:%M:%S')}  {name:<24} {value:>12.1f}")

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="testmqtt-sys-monitor")
client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
client.on_connect = on_connect
client.on_message = on_message
client.connect(HOST, PORT, keepalive=30)
client.loop_forever()

S'abonner dans on_connect est important : après une reconnexion, les abonnements sont rétablis automatiquement. Pour tracer le CSV, quelques lignes de pandas et matplotlib suffisent :

plot.py
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("sys_metrics.csv", names=["ts", "metric", "value"])
df["ts"] = pd.to_datetime(df["ts"], unit="s")
df.pivot_table(index="ts", columns="metric", values="value").plot(subplots=True, figsize=(10, 8))
plt.tight_layout()
plt.show()

Pour un dashboard permanent avec historique et alertes, envoyez les mêmes valeurs dans Prometheus ou InfluxDB plutôt que dans un CSV.

Vérifiez que votre supervision réagit

Un moniteur qui n'a jamais vu de changement n'a pas été testé. Générez une charge connue et vérifiez que les chiffres bougent :

Publier 1 000 messages et observer le débit
for i in $(seq 1 1000); do echo "{\"i\":$i}"; done | \
  mosquitto_pub -h broker.connectmq.com -u "$MQTT_USER" -P "$MQTT_PASS" \
  -t testmqtt/sys-monitor/load -l

Pendant le test, messages/received/s devrait augmenter à peu près du débit auquel vous publiez. Ouvrez dix connexions supplémentaires dans le client en ligne ou avec un simulateur d'appareils, et clients/connected devrait augmenter de dix. Si rien ne bouge, vous lisez probablement un autre nœud que celui sur lequel vous écrivez, ou un cluster remonte des valeurs par nœud.

Les limites de la supervision par $SYS

  • Pas d'historique. $SYS ne montre que l'état actuel. C'est à vous de le stocker.
  • Pas d'alertes. Associez-le à Prometheus, Grafana ou à un script qui vous prévient. Pour des vérifications depuis l'extérieur, voir les health checks du broker.
  • Propre à chaque broker. Les noms de topics et leur signification changent d'un broker à l'autre, et parfois d'une version à l'autre.
  • Surveillé par le système qu'il surveille. Si le broker tombe, $SYS se tait. Un moniteur silencieux doit déclencher une alerte, pas passer pour une journée calme.

Les topics $SYS sont l'observabilité la moins coûteuse qu'offre un broker MQTT. Abonnez-vous une fois à $SYS/# pour voir ce que publie votre broker, gardez un petit script qui transforme les compteurs en débits, et restreignez l'accès pour que seul votre compte de supervision puisse lire l'arborescence.

Questions fréquentes

Pourquoi un abonnement à # n'affiche-t-il pas les messages $SYS ?

La spécification MQTT indique que les abonnements avec joker commençant par # ou + ne doivent pas correspondre aux topics qui commencent par $. Abonnez-vous explicitement à $SYS/#, dans un abonnement séparé.

À quelle fréquence les topics $SYS sont-ils mis à jour ?

Cela dépend du broker. Mosquitto les publie toutes les sys_interval secondes (10 par défaut, 0 les désactive). EMQX utilise son propre réglage sys_msg_interval. Vérifiez la configuration de votre broker.

Les topics $SYS font-ils partie du standard MQTT ?

Non. La spécification réserve seulement les topics commençant par $ à l'usage du broker. Les noms et valeurs sous $SYS sont propres à chaque broker : un dashboard conçu pour Mosquitto ne fonctionnera pas tel quel sur EMQX ou un autre broker.