Retour à l'accueil

Pratique Kafka KRaft microservices Spring Boot

Guide pratique pour déployer un cluster Kafka sur KRaft avec intégration dans des microservices Spring Boot. Architecture complète de boutique en ligne avec rôles producteur/consommateur, configuration docker-compose, création programmatique de sujets.

Kafka en pratique : cluster KRaft + microservices Java
Advertisement 728x90

Implémentation pratique de Kafka dans des microservices Spring Boot

Les développeurs de niveau intermédiaire à senior peuvent déployer rapidement un cluster Kafka en utilisant KRaft et l'intégrer avec quatre services Spring Boot pour une boutique en ligne. Le order-service accepte les commandes via REST API et publie un événement order-placed. Le inventory-service réserve le stock et génère des événements inventory-reserved ou inventory-rejected. Les services analytics-service et notification-service traitent ces événements de manière asynchrone.

Prérequis : Java, Spring Boot, Docker, Docker Compose, Postman. Dépendances : Spring for Apache Kafka, Spring Web.

Docker Compose pour le cluster Kafka

Déploiement de trois brokers Kafka 8.1.0 avec KRaft sans Zookeeper. Chaque broker assume à la fois les rôles de broker et de contrôleur. Kafka-UI sur le port 8086 pour surveiller les topics, partitions et groupes de consommateurs.

Google AdInline article slot

Créez docker-compose.yaml :

services:
  kafka-0:
    image: confluentinc/cp-kafka:8.1.0
    container_name: kafka-0
    hostname: kafka-0
    ports:
      - "29092:29092"
    environment:
      CLUSTER_ID: ZETVJENWRjaECDUEExP_eg
      KAFKA_NODE_ID: 0
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_LISTENERS: INTERNAL://:9091,EXTERNAL://:29092,CONTROLLER://:9093
      KAFKA_CONTROLLER_QUORUM_VOTERS: 0@kafka-0:9093,1@kafka-1:9093,2@kafka-2:9093
      KAFKA_ADVERTISED_LISTENERS: INTERNAL://kafka-0:9091,EXTERNAL://localhost:29092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT,CONTROLLER:PLAINTEXT
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_INTER_BROKER_LISTENER_NAME: INTERNAL
  # Similarly for kafka-1 (port 29093), kafka-2 (port 29094)
  kafka-ui:
    image: provectuslabs/kafka-ui:v0.7.2
    container_name: kafka-ui
    ports:
      - "8086:8080"
    environment:
      KAFKA_CLUSTERS_0_NAME: local
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka-0:9091,kafka-1:9091,kafka-2:9091
      DYNAMIC_CONFIG_ENABLED: true

Commande de démarrage : docker compose up -d. Le cluster est prêt à l'emploi avec 3 partitions et une réplication x3.

Configuration des services Spring Boot

Paramètres communs du producteur

Dans application.properties pour tous les services :

Google AdInline article slot
spring.kafka.bootstrap-servers=localhost:29092,localhost:29093,localhost:29094
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer

Création programmatique des topics

Classe KafkaConfig dans le package config :

@Configuration
public class KafkaConfig {
    @Bean
    public NewTopic orderPlacedTopic() {
        return TopicBuilder.name("order-placed")
            .partitions(3)
            .replicas(3)
            .config(TopicConfig.RETENTION_MS_CONFIG, "86400000")
            .config(TopicConfig.RETENTION_BYTES_CONFIG, "524288000")
            .build();
    }
}

Rétention : 24 heures, 500 Mo par topic. De même pour inventory-reserved, inventory-rejected dans inventory-service.

Implémentation du Order-Service (Producteur)

DTO et événements

public record OrderRequest(String email, String productName, Integer quantity) {}

public record OrderPlacedEvent(String orderId, String email, String productName, Integer quantity) {}

Logique métier

@Service
public class OrderService {
    private final KafkaTemplate<String, OrderPlacedEvent> kafkaTemplate;

    public OrderService(KafkaTemplate<String, OrderPlacedEvent> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }

    public String placeOrder(OrderRequest orderRequest) {
        String orderId = UUID.randomUUID().toString();
        OrderPlacedEvent event = new OrderPlacedEvent(orderId, orderRequest.email(), 
            orderRequest.productName(), orderRequest.quantity());
        kafkaTemplate.send("order-placed", event);
        return orderId;
    }
}

Le contrôleur REST renvoie un 201 Created avec l'orderId instantanément, le traitement s'effectuant de manière asynchrone.

Google AdInline article slot

Inventory-Service : Producteur + Consommateur

Écoute order-placed, vérifie les niveaux de stock et publie inventory-reserved ou inventory-rejected. Nécessite @KafkaListener et un second KafkaTemplate.

Rôles des services :

  • Producteur pur : order-service
  • Consommateur pur : analytics-service, notification-service
  • Producteur+Consommateur : inventory-service

Implémentation du consommateur (analytics, notification)

@KafkaListener(topics = "inventory-reserved")
public void handleInventoryReserved(InventoryReservedEvent event) {
    // Analytics logic
}

Spring Kafka gère automatiquement les offsets, les groupes de consommateurs étant formés par le nom du service.

Test du flux

  • POST /orders avec le JSON { "email": "[email protected]", "productName": "laptop", "quantity": 1 }
  • Surveiller dans Kafka-UI : order-placed → inventory-reserved
  • Les services analytics et notification reçoivent les événements en parallèle

Points clés :

  • KRaft simplifie le déploiement sans Zookeeper
  • 3 partitions + réplication x3 = tolérance aux pannes
  • JsonSerializer pour la valeur, StringSerializer pour la clé
  • Rétention 24h/500 Mo adaptée au développement
  • KafkaTemplate abstrait l'API de bas niveau

— Editorial Team

Advertisement 728x90

Lire ensuite