KafkaDataArchitecture

Kafka : consumer groups et gestion des offsets

1 septembre 2026 · Sphinx-Digital

Les consumer groups sont le mécanisme central qui permet à Kafka de scaler la consommation horizontalement tout en garantissant qu’un message n’est traité qu’une seule fois au sein d’un groupe. Mal compris, ils sont source de doublons, de messages perdus ou de lag qui s’accumule silencieusement.

Consumer groups : le principe

Un consumer group est un ensemble de consommateurs qui lisent collectivement les partitions d’un topic. Kafka garantit qu’une partition est assignée à un seul consommateur dans un groupe à un instant donné.

Topic: commandes (6 partitions)

Consumer Group A (3 instances) :
  Instance 1 → Partitions 0, 1
  Instance 2 → Partitions 2, 3
  Instance 3 → Partitions 4, 5

Consumer Group B (1 instance) :
  Instance 1 → Partitions 0, 1, 2, 3, 4, 5  (toutes)

Les deux groups consomment indépendamment le même topic — chacun maintient ses propres offsets. Ajouter un group B n’impacte pas le group A.

Scalabilité : le nombre de partitions comme limite

Le nombre de consommateurs actifs dans un group est limité par le nombre de partitions du topic. Si vous avez 6 partitions et 8 consommateurs dans un group, 2 consommateurs seront inactifs.

from confluent_kafka import Consumer

consumer = Consumer({
    'bootstrap.servers': 'kafka:9092',
    'group.id': 'order-processor',
    'auto.offset.reset': 'earliest',
    'enable.auto.commit': False,   # commit manuel pour contrôler exactement ce qui est acquitté
})

consumer.subscribe(['commandes'])

try:
    while True:
        msg = consumer.poll(timeout=1.0)
        if msg is None:
            continue
        if msg.error():
            print(f"Erreur : {msg.error()}")
            continue

        # Traitement du message
        process_order(msg.value())

        # Commit manuel après traitement réussi
        consumer.commit(asynchronous=False)

finally:
    consumer.close()

Stratégies de commit des offsets

C’est le point le plus critique pour éviter doublons et pertes de messages.

Auto-commit (enable.auto.commit=True) — Kafka commit automatiquement toutes les X secondes. Risque : si le process crash entre le poll et le commit, les messages non commités seront retraités au redémarrage.

Commit synchrone après traitement — garantit qu’on ne commite que ce qui a été réellement traité. Plus lent mais fiable.

# At-least-once : retraitement possible mais pas de perte
msg = consumer.poll(1.0)
process_order(msg.value())         # traitement
consumer.commit(asynchronous=False)  # commit après succès

# Si process_order() lève une exception, pas de commit
# → le message sera retraité au prochain démarrage

Commit par batch — amortit le coût du commit sur plusieurs messages :

BATCH_SIZE = 100
messages = []

while True:
    msg = consumer.poll(1.0)
    if msg:
        messages.append(msg)

    if len(messages) >= BATCH_SIZE:
        for m in messages:
            process_order(m.value())
        consumer.commit(asynchronous=False)
        messages.clear()

Consumer lag : détecter qu’on prend du retard

Le lag est la différence entre l’offset du dernier message produit (log end offset) et l’offset du dernier message commité par le consommateur. Un lag qui croît indique que les consommateurs ne suivent pas le rythme de production.

# Vérifier le lag de tous les groupes
kafka-consumer-groups.sh \
  --bootstrap-server kafka:9092 \
  --describe \
  --group order-processor

# OUTPUT :
# GROUP           TOPIC       PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG
# order-processor commandes   0          1024            1030            6
# order-processor commandes   1          897             900             3

Rebalancing : ce qui se passe quand un consommateur rejoint ou quitte le groupe

Un rebalancing redistribue les partitions entre les consommateurs actifs. Il se déclenche quand :

  • Un consommateur rejoint le groupe
  • Un consommateur quitte ou tombe (heartbeat timeout)
  • Les partitions du topic changent

Pendant le rebalancing, tous les consommateurs du groupe sont mis en pause. Sur un group avec beaucoup de partitions et un traitement lent, ce peut être problématique.

# Augmenter les timeouts pour éviter les rebalancings intempestifs
consumer = Consumer({
    'session.timeout.ms': 30000,        # déclarer mort après 30s sans heartbeat
    'heartbeat.interval.ms': 10000,     # heartbeat toutes les 10s
    'max.poll.interval.ms': 300000,     # 5min max entre deux polls (traitement long)
})

Stratégie d’assignation des partitions

Kafka propose plusieurs stratégies :

  • RangeAssignor (défaut) : distribue des ranges contigus de partitions. Peut créer un déséquilibre.
  • RoundRobinAssignor : distribue partition par partition en round-robin.
  • StickyAssignor : minimize les mouvements de partitions lors d’un rebalancing — meilleur pour les consumers avec état.
  • CooperativeStickyAssignor : rebalancing incrémental, les partitions non déplacées continuent à être consommées pendant le rebalancing.
consumer = Consumer({
    'partition.assignment.strategy': 'cooperative-sticky',
})

Notre formation Kafka couvre les consumer groups et les stratégies de consommation avec des ateliers sur des clusters réels.