Volver al inicio

Práctica de Kafka KRaft con microservicios Spring Boot

Guía práctica para desplegar clúster de Kafka en KRaft con integración en microservicios Spring Boot. Arquitectura completa de tienda en línea con roles producer/consumer, configuración docker-compose, creación programática de tópicos.

Kafka en la práctica: clúster KRaft + microservicios Java
Advertisement 728x90

Implementación práctica de Kafka en microservicios Spring Boot

Los desarrolladores de nivel medio a senior pueden desplegar rápidamente un clúster de Kafka usando KRaft e integrarlo con cuatro servicios de Spring Boot para una tienda en línea. El order-service acepta pedidos vía REST API y publica un evento de order-placed. El inventory-service reserva existencias y genera eventos de inventory-reserved o inventory-rejected. Los servicios analytics-service y notification-service procesan estos eventos de forma asíncrona.

Requisitos: Java, Spring Boot, Docker, Docker Compose, Postman. Dependencias: Spring for Apache Kafka, Spring Web.

Docker Compose para el clúster de Kafka

Despliegue de tres brokers de Kafka 8.1.0 con KRaft sin Zookeeper. Cada broker realiza tanto roles de broker como de controller. Kafka-UI en el puerto 8086 para monitorear topics, particiones y consumer groups.

Google AdInline article slot

Crea 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

Comando de inicio: docker compose up -d. El clúster está listo para carga con 3 particiones y replicación x3.

Configuración de servicios Spring Boot

Configuraciones comunes de Producer

En application.properties para todos los servicios:

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

Creación programática de Topics

Clase KafkaConfig en el paquete 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();
    }
}

Retención: 24 horas, 500 MB por topic. Similar para inventory-reserved, inventory-rejected en inventory-service.

Implementación de Order-Service (Producer)

DTOs y Events

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

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

Lógica de negocio

@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;
    }
}

El controlador REST devuelve 201 Created con el orderId de inmediato, mientras el procesamiento ocurre de forma asíncrona.

Google AdInline article slot

Inventory-Service: Producer + Consumer

Escucha order-placed, verifica niveles de stock y publica inventory-reserved o inventory-rejected. Requiere @KafkaListener y un segundo KafkaTemplate.

Roles de servicios:

  • Productor puro: order-service
  • Consumidor puro: analytics-service, notification-service
  • Productor+Consumidor: inventory-service

Implementación de Consumidor (analytics, notification)

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

Spring Kafka gestiona automáticamente los offsets, con consumer groups formados por el nombre del servicio.

Probando el flujo

  • POST /orders con JSON { "email": "[email protected]", "productName": "laptop", "quantity": 1 }
  • Monitorea en Kafka-UI: order-placed → inventory-reserved
  • Analytics y notification reciben eventos en paralelo

Puntos clave:

  • KRaft simplifica el despliegue sin Zookeeper
  • 3 particiones + replicación x3 = tolerancia a fallos
  • JsonSerializer para value, StringSerializer para key
  • Retención de 24 h / 500 MB adecuada para desarrollo
  • KafkaTemplate abstrae la API de bajo nivel

— Editorial Team

Advertisement 728x90

Leer después