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.
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:
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.
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
/orderscon 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
Aún no hay comentarios.