Aller au contenu principal
KAFKA

Introduction à Apache Kafka : Guide Complet du Streaming Distribué

Découvrez Apache Kafka de A à Z : concepts fondamentaux, architecture distribuée, mise en place pratique, et cas d'usage réels pour construire des systèmes de streaming robustes.

Florian Courouge
18 min de lecture
👁️1.3k vues
kafka
streaming
architecture
microservices
distributed-systems
event-driven
Niveau:
Débutant

Introduction à Apache Kafka : Guide Complet du Streaming Distribué

Apache Kafka est devenu l'épine dorsale des architectures de données modernes. Cette plateforme de streaming distribuée permet de construire des pipelines de données en temps réel, traiter des millions d'événements par seconde, et connecter des systèmes hétérogènes avec une fiabilité exceptionnelle.

Kafka Architecture Overview

Qu'est-ce que Kafka ?

Définition et Origines

Apache Kafka est une plateforme de streaming distribuée développée initialement par LinkedIn en 2011, puis open-sourcée. Elle permet de :

  • Publier et souscrire à des flux d'événements (publish-subscribe)
  • Stocker des flux d'événements de manière durable et fiable
  • Traiter des flux d'événements en temps réel

Différence avec les systèmes de messaging traditionnels

Contrairement aux message brokers classiques (RabbitMQ, ActiveMQ), Kafka apporte :

| Caractéristique | Kafka | Message Brokers Traditionnels | |----------------|-------|------------------------------| | Persistance | Stockage sur disque durable | Messages volatiles | | Débit | Millions de msg/sec | Milliers de msg/sec | | Scalabilité | Horizontale (partitions) | Verticale principalement | | Replay | Messages rejouables | Une seule lecture | | Latence | ~10ms | Variable |

Concepts Fondamentaux

Architecture de Kafka : Vue d'ensemble

┌─────────────────────────────────────────────────────────────────┐
│                        KAFKA CLUSTER                             │
│                                                                  │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐                 │
│  │ Broker 1 │    │ Broker 2 │    │ Broker 3 │                 │
│  │          │    │          │    │          │                 │
│  │ Topic A  │    │ Topic A  │    │ Topic B  │                 │
│  │ P0, P2   │    │ P1, P3   │    │ P0, P1   │                 │
│  └──────────┘    └──────────┘    └──────────┘                 │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘
       ▲                    ▲                    ▲
       │                    │                    │
  ┌────┴────┐          ┌────┴────┐         ┌────┴────┐
  │Producer1│          │Producer2│         │Producer3│
  └─────────┘          └─────────┘         └─────────┘

       │                    │                    │
       ▼                    ▼                    ▼
  ┌─────────┐          ┌─────────┐         ┌─────────┐
  │Consumer1│          │Consumer2│         │Consumer3│
  │ Group A │          │ Group A │         │ Group B │
  └─────────┘          └─────────┘         └─────────┘

1. Topics et Partitions

Topic : Canal logique de données, comparable à une table de base de données.

# Un topic "orders" peut contenir toutes les commandes e-commerce
orders-topic:
  - partition-0: [order1, order4, order7, ...]
  - partition-1: [order2, order5, order8, ...]
  - partition-2: [order3, order6, order9, ...]

Partition : Division physique d'un topic pour la scalabilité et le parallélisme.

  • Chaque partition est ordonnée et immuable
  • Les messages sont ajoutés à la fin (append-only log)
  • Chaque message a un offset unique dans sa partition
Partition 0:
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│  0  │  1  │  2  │  3  │  4  │  5  │  6  │ ← Offsets
├─────┼─────┼─────┼─────┼─────┼─────┼─────┤
│msg1 │msg2 │msg3 │msg4 │msg5 │msg6 │msg7 │
└─────┴─────┴─────┴─────┴─────┴─────┴─────┘
                                        ▲
                                   Current offset

2. Producers : Publier des Événements

Les producers publient des messages vers Kafka.

Exemple Java :

// Configuration du producer
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("acks", "all"); // Garantie de durabilité maximale

KafkaProducer<String, String> producer = new KafkaProducer<>(props);

// Envoyer un message
ProducerRecord<String, String> record =
    new ProducerRecord<>("orders", "order-123", "{\"amount\": 99.99}");

producer.send(record, (metadata, exception) -> {
    if (exception == null) {
        System.out.printf("Message envoyé vers partition %d, offset %d%n",
            metadata.partition(), metadata.offset());
    } else {
        exception.printStackTrace();
    }
});

producer.close();

Stratégies de partitioning :

// 1. Par clé (garantit l'ordre pour une clé donnée)
producer.send(new ProducerRecord<>("orders", customerId, order));

// 2. Round-robin (si pas de clé)
producer.send(new ProducerRecord<>("logs", null, logMessage));

// 3. Partitioner personnalisé
public class RegionPartitioner implements Partitioner {
    public int partition(String topic, Object key, byte[] keyBytes,
                        Object value, byte[] valueBytes, Cluster cluster) {
        String region = extractRegion(value);
        return region.hashCode() % cluster.partitionCountForTopic(topic);
    }
}

3. Consumers : Lire des Événements

Les consumers lisent des messages depuis Kafka.

Exemple Java :

// Configuration du consumer
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "order-processing-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("auto.offset.reset", "earliest"); // Lire depuis le début

KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("orders"));

// Boucle de consommation
try {
    while (true) {
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
        for (ConsumerRecord<String, String> record : records) {
            System.out.printf("Reçu: key=%s, value=%s, partition=%d, offset=%d%n",
                record.key(), record.value(), record.partition(), record.offset());

            // Traiter le message
            processOrder(record.value());
        }

        // Commit manuel des offsets
        consumer.commitSync();
    }
} finally {
    consumer.close();
}

4. Consumer Groups : Parallélisme et Scalabilité

Les consumers d'un même groupe partagent la charge de lecture.

Topic "orders" (3 partitions):
┌────────────┐  ┌────────────┐  ┌────────────┐
│Partition 0 │  │Partition 1 │  │Partition 2 │
└─────┬──────┘  └─────┬──────┘  └─────┬──────┘
      │               │               │
      └───────┬───────┴───────┬───────┘
              │               │
      ┌───────▼──────┐ ┌─────▼────────┐
      │ Consumer 1   │ │ Consumer 2   │
      │ (Group A)    │ │ (Group A)    │
      │ Reads P0,P1  │ │ Reads P2     │
      └──────────────┘ └──────────────┘

Règles importantes :

  • Un consumer peut lire plusieurs partitions
  • Une partition ne peut être lue que par un seul consumer par groupe
  • Plusieurs groupes peuvent lire le même topic indépendamment

5. Brokers et Cluster

Broker : Serveur Kafka qui stocke les données et sert les clients.

Cluster : Ensemble de brokers travaillant ensemble.

# Cluster de 3 brokers
Broker 1 (id=1): Leader pour partitions [orders-0, users-1]
Broker 2 (id=2): Leader pour partitions [orders-1, users-2]
Broker 3 (id=3): Leader pour partitions [orders-2, users-0]

6. Réplication : Haute Disponibilité

Kafka réplique les partitions sur plusieurs brokers.

Topic: orders, Partition: 0, Replication Factor: 3

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   Broker 1   │     │   Broker 2   │     │   Broker 3   │
│   (Leader)   │────▶│  (Follower)  │────▶│  (Follower)  │
│              │     │              │     │              │
│ orders-0     │     │ orders-0     │     │ orders-0     │
│ (in-sync)    │     │ (in-sync)    │     │ (in-sync)    │
└──────────────┘     └──────────────┘     └──────────────┘

Mise en Place Pratique

Installation avec Docker Compose

version: '3.8'

services:
  # ZooKeeper (coordination du cluster)
  zookeeper:
    image: confluentinc/cp-zookeeper:7.4.0
    container_name: zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    ports:
      - "2181:2181"
    volumes:
      - zookeeper-data:/var/lib/zookeeper/data
      - zookeeper-logs:/var/lib/zookeeper/log

  # Kafka Broker
  kafka:
    image: confluentinc/cp-kafka:7.4.0
    container_name: kafka
    depends_on:
      - zookeeper
    ports:
      - "9092:9092"
      - "9093:9093"
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181

      # Listeners configuration
      KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,PLAINTEXT_HOST://0.0.0.0:9093
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:9093
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT

      # Configuration cluster
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0

      # Performance tuning
      KAFKA_NUM_NETWORK_THREADS: 3
      KAFKA_NUM_IO_THREADS: 8
      KAFKA_SOCKET_SEND_BUFFER_BYTES: 102400
      KAFKA_SOCKET_RECEIVE_BUFFER_BYTES: 102400
      KAFKA_SOCKET_REQUEST_MAX_BYTES: 104857600

      # Retention
      KAFKA_LOG_RETENTION_HOURS: 168
      KAFKA_LOG_SEGMENT_BYTES: 1073741824

    volumes:
      - kafka-data:/var/lib/kafka/data

  # Kafka UI (optionnel mais utile)
  kafka-ui:
    image: provectuslabs/kafka-ui:latest
    container_name: kafka-ui
    depends_on:
      - kafka
    ports:
      - "8080:8080"
    environment:
      KAFKA_CLUSTERS_0_NAME: local
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
      KAFKA_CLUSTERS_0_ZOOKEEPER: zookeeper:2181

volumes:
  zookeeper-data:
  zookeeper-logs:
  kafka-data:
# Démarrer le cluster
docker-compose up -d

# Vérifier les logs
docker-compose logs -f kafka

# Créer un topic
docker exec kafka kafka-topics --create \
  --bootstrap-server localhost:9092 \
  --topic test-topic \
  --partitions 3 \
  --replication-factor 1

# Lister les topics
docker exec kafka kafka-topics --list \
  --bootstrap-server localhost:9092

# Produire des messages
docker exec -it kafka kafka-console-producer \
  --bootstrap-server localhost:9092 \
  --topic test-topic

# Consommer des messages
docker exec -it kafka kafka-console-consumer \
  --bootstrap-server localhost:9092 \
  --topic test-topic \
  --from-beginning

Cas d'Usage Réels

1. Architecture Event-Driven de Microservices

┌─────────────┐        ┌─────────────┐        ┌─────────────┐
│   Orders    │──────▶ │    Kafka    │ ──────▶│  Inventory  │
│  Service    │        │   (events)  │        │   Service   │
└─────────────┘        └──────┬──────┘        └─────────────┘
                              │
                              ├──────────────▶ ┌─────────────┐
                              │                │  Shipping   │
                              │                │   Service   │
                              │                └─────────────┘
                              │
                              └──────────────▶ ┌─────────────┐
                                               │ Notification│
                                               │   Service   │
                                               └─────────────┘

Avantages :

  • Couplage faible entre services
  • Scalabilité indépendante
  • Résilience aux pannes

2. Log Aggregation et Centralisation

# Collecter les logs de tous les services
Application Servers → Filebeat → Kafka → Logstash → Elasticsearch
                                   │
                                   └─────────→ S3 (archivage long terme)

3. Streaming Analytics en Temps Réel

// Kafka Streams : Compter les commandes par région en temps réel
StreamsBuilder builder = new StreamsBuilder();

KStream<String, Order> orders = builder.stream("orders");

KTable<String, Long> ordersByRegion = orders
    .groupBy((key, order) -> order.getRegion())
    .count();

ordersByRegion.toStream()
    .to("orders-by-region-stats");

4. Change Data Capture (CDC)

PostgreSQL → Debezium → Kafka → Stream Processing → Analytics DB
                         │
                         └──────→ Data Lake (S3/HDFS)

5. Event Sourcing et CQRS

// Event Sourcing : Tous les changements d'état sont des événements
public class AccountEventSourcing {

    public void handleCommand(CreateAccountCommand cmd) {
        // Publier un événement
        producer.send(new ProducerRecord<>(
            "account-events",
            cmd.getAccountId(),
            new AccountCreatedEvent(cmd.getAccountId(), cmd.getOwner())
        ));
    }

    public void handleCommand(DepositMoneyCommand cmd) {
        producer.send(new ProducerRecord<>(
            "account-events",
            cmd.getAccountId(),
            new MoneyDepositedEvent(cmd.getAccountId(), cmd.getAmount())
        ));
    }
}

Avantages et Limitations

Avantages de Kafka

Performance Exceptionnelle

  • Débit : Plusieurs millions de messages/seconde
  • Latence : ~10ms en conditions normales
  • Optimisations : Zero-copy, compression, batching

Scalabilité Horizontale

  • Ajout de brokers sans interruption
  • Partitioning pour parallélisme
  • Consumer groups pour distribution de charge

Durabilité et Fiabilité

  • Persistance sur disque
  • Réplication configurable
  • Garanties de livraison (at-least-once, exactly-once)

Flexibilité

  • Messages rejouables (replay)
  • Rétention configurable (jours, semaines, indéfini)
  • Intégrations riches (Kafka Connect, Streams)

Limitations et Considérations

Complexité Opérationnelle

  • Cluster à gérer (brokers, ZooKeeper/KRaft)
  • Monitoring nécessaire
  • Tuning selon les cas d'usage

Latence pour Petits Messages

  • Optimisé pour le débit, pas latence ultra-faible (<1ms)
  • Batching peut introduire des délais

Ordre Garanti par Partition Uniquement

  • Nécessite un partitioning strategy adapté
  • Pas d'ordre global sur un topic

Consommation de Ressources

  • JVM (heap memory)
  • Stockage disque important
  • Bande passante réseau

Bonnes Pratiques

1. Nommage des Topics

# Convention recommandée: <domain>.<entity>.<event-type>
ecommerce.orders.created
ecommerce.orders.updated
ecommerce.orders.cancelled

# Éviter:
orders  # Trop générique
my-topic  # Pas de contexte

2. Configuration de Rétention

# Par temps (7 jours)
kafka-configs --alter --entity-type topics --entity-name orders \
  --add-config retention.ms=604800000

# Par taille (10GB)
kafka-configs --alter --entity-type topics --entity-name logs \
  --add-config retention.bytes=10737418240

3. Replication Factor

# Production: minimum 3
--replication-factor 3

# Dev/Test: 1 suffit
--replication-factor 1

4. Gestion des Erreurs

// Retry avec backoff exponentiel
props.put("retries", Integer.MAX_VALUE);
props.put("retry.backoff.ms", 100);
props.put("delivery.timeout.ms", 120000);

// Dead Letter Queue pour messages en échec
try {
    processMessage(record);
} catch (Exception e) {
    sendToDeadLetterQueue(record, e);
}

Ressources et Prochaines Étapes

Approfondissement Recommandé

  1. Architecture Interne : Comprendre le storage engine, réplication, ISR
  2. Performance Tuning : Optimiser producers, consumers, brokers
  3. Kafka Streams : Stream processing avec l'API Streams
  4. Kafka Connect : Intégrations avec bases de données, systèmes externes
  5. Monitoring : Prometheus, Grafana, métriques JMX

Livres et Documentation

Conclusion

Apache Kafka transforme la manière dont nous construisons des systèmes distribués modernes. En combinant :

  • Haute performance : Millions de messages/seconde
  • Fiabilité : Réplication et durabilité
  • Scalabilité : Architecture horizontale
  • Flexibilité : Nombreux cas d'usage

Kafka devient un choix incontournable pour les architectures event-driven, le streaming en temps réel, et l'intégration de systèmes hétérogènes.

La maîtrise de Kafka ouvre la porte à des architectures de données modernes, capables de gérer des volumes massifs tout en restant réactives et résilientes.


Cet article fait partie d'une série complète sur Apache Kafka. Consultez mes autres guides pour approfondir l'architecture interne, le monitoring en production, et les patterns avancés.