Aller au contenu

Ingestion et messaging

Le pipeline d'ingestion est le premier maillon critique : il doit absorber des milliers de connexions simultanées, survivre aux pics, et transmettre les événements sans perte ni duplication.


MQTT en production : scaler au-delà d'un broker unique

Un broker MQTT open source standard (Mosquitto) tient quelques milliers de connexions. Dès que le parc dépasse 10 000 appareils ou que la haute disponibilité est requise, il faut passer à un broker conçu pour le clustering.

EMQX

EMQX est un broker MQTT écrit en Erlang/OTP, le même runtime que RabbitMQ. Sa particularité : le modèle acteur d'Erlang gère chaque connexion dans un processus léger indépendant, ce qui donne une résilience naturelle aux pannes partielles.

Caractéristiques clés :

  • Clustering natif par Mnesia (base de données distribuée Erlang)
  • 100 millions de connexions simultanées sur un cluster de 20 nœuds
  • Dashboard web intégré, API REST pour la gestion
  • Règles de routage internes (Rule Engine) vers Kafka, InfluxDB, webhooks
  • Protocoles supportés : MQTT 3.1/3.1.1/5.0, MQTT-SN, CoAP, LwM2M

Configuration cluster EMQX (3 nœuds) :

# emqx.conf — nœud 1
node:
  name: emqx@10.0.1.1
  cookie: "secret_cluster_cookie"

cluster:
  name: iot_prod
  discovery_strategy: static
  static:
    seeds:
      - "emqx@10.0.1.1"
      - "emqx@10.0.1.2"
      - "emqx@10.0.1.3"

listeners:
  tcp:
    default:
      bind: "0.0.0.0:1883"
      max_connections: 1000000
  ssl:
    default:
      bind: "0.0.0.0:8883"
      ssl_options:
        cacertfile: "/etc/emqx/certs/ca.crt"
        certfile: "/etc/emqx/certs/server.crt"
        keyfile: "/etc/emqx/certs/server.key"
        verify: verify_peer

VerneMQ

VerneMQ est un autre broker MQTT Erlang, plus orienté opérateur avec un système de plugins Lua. Il est particulièrement adapté aux télécoms et aux plateformes multi-tenant.


Kafka et Redpanda comme bus d'événements

MQTT résout l'ingestion (connexions persistantes, QoS, rétention de messages). Mais il n'est pas conçu pour le traitement en aval : pas de partitionnement par clé, pas de replay, pas de groupes de consommateurs avec offsets.

C'est le rôle de Kafka (ou de son équivalent compatible Redpanda).

Rôle de Kafka dans le pipeline IoT

flowchart LR
    subgraph "Appareils"
        D1[Device 001]
        D2[Device 002]
        D3[Device 003]
    end

    subgraph "Ingestion MQTT"
        B[EMQX cluster\n3 nœuds]
        BRIDGE[EMQX Rule Engine\nMQTT → Kafka bridge]
    end

    subgraph "Bus Kafka"
        T1[Topic\niot.telemetry.temperature]
        T2[Topic\niot.telemetry.pressure]
        T3[Topic\niot.events.alerts]
    end

    subgraph "Consommateurs"
        C1[Stream processor\nFlink]
        C2[Sink InfluxDB\nKafka Connect]
        C3[Service alertes]
        C4[Archive S3]
    end

    D1 -->|MQTT TLS| B
    D2 -->|MQTT TLS| B
    D3 -->|MQTT TLS| B
    B --> BRIDGE
    BRIDGE --> T1
    BRIDGE --> T2
    BRIDGE --> T3
    T1 --> C1
    T1 --> C2
    T2 --> C1
    T2 --> C2
    T3 --> C3
    T1 --> C4
    T2 --> C4

Partitionnement par device

Le partitionnement est la clé de la scalabilité Kafka. Pour l'IoT, la règle est simple : partitionner par device ID.

Partition key = device_id (ex: "sensor-factory-A-042")

Cela garantit que tous les messages d'un même capteur arrivent dans le même ordre au même consommateur. Avec 100 partitions et 10 000 devices, chaque partition traite en moyenne 100 devices.

Exemple de producer Kafka (Python confluent-kafka) :

from confluent_kafka import Producer
import json, time

conf = {
    'bootstrap.servers': 'kafka1:9092,kafka2:9092,kafka3:9092',
    'acks': 'all',           # durabilité maximale
    'compression.type': 'lz4',
    'batch.size': 65536,     # 64 Ko par batch
    'linger.ms': 5           # attendre 5 ms pour remplir le batch
}

producer = Producer(conf)

def publish_telemetry(device_id: str, payload: dict):
    producer.produce(
        topic='iot.telemetry',
        key=device_id.encode(),          # clé de partitionnement
        value=json.dumps(payload).encode(),
        timestamp=int(time.time() * 1000)
    )
    producer.poll(0)  # déclenche les callbacks non-bloquant

Redpanda : Kafka sans ZooKeeper

Redpanda est un broker compatible API Kafka, réécrit en C++. Ses avantages pour l'IoT :

  • Démarrage en une seule commande (docker run redpandadata/redpanda)
  • Pas de ZooKeeper ni de KRaft à gérer
  • Latence médiane 2-3× plus faible que Kafka en workload IoT typique
  • Consommation mémoire réduite de 40 % environ

Il est 100 % compatible avec l'API Kafka 3.x : les producteurs et consommateurs existants fonctionnent sans modification.


Back-pressure et contrôle de flux

Le back-pressure est le mécanisme par lequel un consommateur lent signale à l'ensemble de la chaîne qu'il est saturé, afin d'éviter la perte de données.

Sources de back-pressure dans un pipeline IoT

  1. Pic de reconnexion : après une coupure réseau, tous les devices se reconnectent simultanément et envoient leur backlog store-and-forward.
  2. Consommateur lent : une requête d'écriture InfluxDB prend 200 ms à la place de 10 ms en raison d'un index fragmenté.
  3. Traitement lourd : un job Flink exécute une jointure complexe qui ralentit la consommation du topic Kafka.

Stratégies de gestion

Mécanisme Description Brique
Offset lag monitoring Surveiller le retard des consommateurs Kafka Kafka / Redpanda metrics
Consumer group throttling Limiter le débit d'un groupe consommateur max.poll.records, fetch.max.bytes
Topic retention Kafka conserve les messages N heures pour permettre le rattrapage retention.ms
Circuit breaker Couper le flux vers un sink défaillant et rerouter Kafka Connect / custom
MQTT QoS 1 + clean session false Garantir la remise même si le broker redémarre EMQX
Flow control MQTT 5.0 Le broker envoie un DISCONNECT avec Quota Exceeded EMQX + MQTT 5.0

Comparaison MQTT broker vs Kafka

Dimension MQTT (EMQX cluster) Kafka / Redpanda
Modèle Pub/sub orienté connexion Log distribué avec offsets
Protocole client MQTT, CoAP, LwM2M Kafka protocol (TCP binaire)
Rétention des messages Session / QoS seulement Jours ou semaines (configurable)
Replay Non (sauf MQTT 5.0 retained) Oui, depuis n'importe quel offset
Partitionnement Par topic uniquement Par topic + clé (partition key)
Nombre de consommateurs Illimité (fan-out natif) Limité par nb partitions / groupe
Latence typique < 10 ms 5-50 ms (batch)
Cas d'usage IoT Ingestion devices → broker Bus inter-services, traitement, archive
Complexité opérationnelle Modérée Élevée (mais Redpanda simplifie)
Débit max testé 5M msg/s (cluster 20 nœuds) 10M msg/s (cluster 10 nœuds)

Règle d'or : MQTT pour la connexion device-to-cloud, Kafka pour tout ce qui vient après.


Sérialisation des messages

Le format de sérialisation a un impact direct sur le débit et la bande passante.

Format Taille typique (100 champs) Lisibilité Schema évolution Usage IoT
JSON 2-5 Ko Humain Flexible Debug, prototypage
MessagePack 1-2 Ko Binaire Flexible Devices contraints
Avro 200-500 o Binaire Schema Registry Pipeline Kafka industriel
Protobuf 150-400 o Binaire Rétrocompatible Haute performance
CBOR 800-1 500 o Binaire Flexible Embedded + CoAP

Pour un pipeline IoT en production, Avro avec Schema Registry Confluent (ou Karapace pour Redpanda) est le standard : il valide les messages à l'écriture et garantit la compatibilité lors des évolutions de schéma.


Ce qu'il faut retenir

  • EMQX cluster + MQTT 5.0 couvre l'ingestion jusqu'à plusieurs millions de devices.
  • Kafka (ou Redpanda) prend le relais comme bus inter-services avec replay et partitionnement.
  • Le back-pressure doit être anticipé dès la conception : offset lag, retention, QoS.

Chapitre suivant : Stockage time-series — choisir et configurer la base de données adaptée aux séries temporelles IoT.