Guia prático do Apache Kafka com Spring Boot: conceitos fundamentais, dependências Gradle, configuração YAML, exemplos de Producer e Consumer, boas práticas e instruções para execução local com Docker.
🧐 O que é? Qual o uso?
O Apache Kafka é uma plataforma de mensageria distribuída, open source, projetada para alta capacidade de envio e processamento de mensagens. Possui alta tolerância a falhas e é utilizada por grandes empresas que enfrentam desafios com grandes volumes de dados.
Conceitos Fundamentais
Antes de começar, é importante entender alguns conceitos básicos do Kafka:
Tópico: Categoria ou canal para onde as mensagens são publicadas
Partição: Divisão de um tópico que permite paralelismo e escalabilidade
Producer: Aplicação que publica mensagens em um tópico
Consumer: Aplicação que consome mensagens de um tópico
Consumer Group: Grupo de consumidores que trabalham juntos para consumir mensagens de um tópico, permitindo balanceamento de carga e tolerância a falhas
Offset: Posição de uma mensagem dentro de uma partição
Broker: Servidor Kafka que armazena e serve dados
Kafka vs Filas Tradicionais
Característica
Filas Tradicionais
Apache Kafka
Persistência
Temporária
Persistente
Escalabilidade
Limitada
Alta (horizontal)
Throughput
Baixo/Médio
Alto
Ordem
Garantida por fila
Garantida por partição
Replay
Não suportado
Suportado (via offsets)
Casos de Uso Típicos
Event sourcing: Captura de eventos para reconstrução do estado
Streaming em tempo real: Processamento contínuo de dados
Log aggregation: Centralização de logs de múltiplos serviços
Mensageria entre microservices: Comunicação assíncrona e desacoplada
Pipeline de dados: ETL e processamento de dados em batch
🛠️ Exemplo: Criando uma aplicação Spring Boot com Spring Kafka
Veja um passo a passo básico para criar uma aplicação simples usando Spring Boot e Spring Kafka:
// ProducerService.javaimportorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.kafka.core.KafkaTemplate;importorg.springframework.stereotype.Service;@ServicepublicclassProducerService{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(ProducerService.class);@AutowiredprivateKafkaTemplate<String,String>kafkaTemplate;publicvoidsendMessage(Stringtopic,Stringmessage){kafkaTemplate.send(topic,message).addCallback(result->logger.info("Mensagem enviada para o tópico {}: {}",topic,message),failure->logger.error("Falha ao enviar mensagem para o tópico {}",topic,failure));}}
Este é um exemplo de Produtor, que envia mensagens para um tópico Kafka. O KafkaTemplate é usado para enviar mensagens de forma assíncrona. O callback permite lidar com sucesso e falhas de forma robusta.
// ConsumerService.javaimportorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.kafka.annotation.KafkaListener;importorg.springframework.kafka.support.Acknowledgment;importorg.springframework.kafka.support.KafkaHeaders;importorg.springframework.messaging.handler.annotation.Header;importorg.springframework.messaging.handler.annotation.Payload;importorg.springframework.stereotype.Service;@ServicepublicclassConsumerService{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(ConsumerService.class);@KafkaListener(topics="meu-topico",groupId="exemplo-grupo")publicvoidlisten(@PayloadStringmessage,@Header(KafkaHeaders.RECEIVED_TOPIC)Stringtopic,@Header(KafkaHeaders.RECEIVED_PARTITION)intpartition,@Header(KafkaHeaders.OFFSET)longoffset,Acknowledgmentacknowledgment){try{logger.info("Mensagem recebida | Tópico: {} | Partição: {} | Offset: {} | Conteúdo: {}",topic,partition,offset,message);// Processar a mensagem aqui// Confirma o processamento da mensagemacknowledgment.acknowledge();}catch(Exceptione){logger.error("Erro ao processar mensagem",e);// Em produção, você pode optar por não fazer acknowledge// para que a mensagem seja reprocessada}}}
Este é um exemplo de Consumidor, que escuta mensagens de um tópico específico. O método listen será chamado sempre que uma nova mensagem for recebida. O uso de Acknowledgment permite controle fino sobre quando a mensagem é considerada processada.
🐳 Rodando Kafka com Docker
Para rodar o Kafka localmente, você pode usar o Docker. Lembre-se de iniciar o Kafka localmente antes de rodar a aplicação.
Opção 1: Usando docker-compose
Crie o arquivo docker-compose.yml na raiz do projeto:
# Se usando docker-composedocker-compose down
# Se usando comandos diretosdocker stop kafka zookeeper
docker rm kafka zookeeper
✅ Testando a aplicação
Criar um endpoint para envio de mensagens
1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController@RequestMapping("/api/messages")publicclassMessageController{@AutowiredprivateProducerServiceproducerService;@PostMappingpublicResponseEntity<String>sendMessage(@RequestBodyStringmessage){producerService.sendMessage("meu-topico",message);returnResponseEntity.ok("Mensagem enviada com sucesso");}}
Testar com curl
1
curl -X POST http://localhost:8080/api/messages -H "Content-Type: application/json" -d "Olá Kafka!"
Você deve ver a mensagem sendo logada pelo consumer.
🔧 Boas Práticas
Para Produtor
Use acks: all em ambientes de produção para garantir durabilidade
Configure retries para lidar com falhas transitórias
Habilite compressão para grandes volumes de dados
Use particionamento quando precisar de paralelismo
Implemente idempotência no lado do consumidor
Para Consumidor
Desabilite auto-commit (enable-auto-commit: false) para processamento atômico
Use acknowledgment explícito para confirmar processamento
Configure timeouts adequados para evitar timeouts de sessão
Monitore lags para identificar consumidores lentos
Implemente Dead Letter Queue para mensagens que não podem ser processadas
@ServicepublicclassConsumerService{@KafkaListener(topics="meu-topico",groupId="exemplo-grupo",errorHandler="kafkaErrorHandler")publicvoidlisten(Stringmessage,Acknowledgmentacknowledgment){try{// Processar mensagemacknowledgment.acknowledge();}catch(Exceptione){// Falha ao processar - não acknowledge para reprocessar// ou enviar para Dead Letter Queue manualmentethrowe;}}}@BeanpublicKafkaListenerErrorHandlerkafkaErrorHandler(){return(message,exception)->{log.error("Erro no consumer: {}",exception.getMessage());// Lógica de tratamento de erroreturnnull;};}
🔍 Troubleshooting
Problemas Comuns
Connection refused: Verifique se o Kafka está rodando e a porta está correta
Consumer não recebe mensagens: Verifique o group-id e auto-offset-reset
Offset out of range: O tópico pode ter sido recriado, considere usar earliest
SerializationException: Verifique se os serializers/deserializers correspondem