Kafka Avançado com Spring Boot: Retry, DLQ, Partitions e Escalabilidade (Guia Completo com Projeto Prático)

Kafka Avançado com Spring Boot: Retry, DLQ, Partitions e Escalabilidade (Guia Completo com Projeto Prático)

Aprenda Kafka avançado com Spring Boot através de um projeto prático completo, implementando retry, dead letter queue (DLQ), partitions, consumidores concorrentes e estratégias de escalabilidade.

nexucodeplay04 de maio de 2026
Compartilhar:LinkedInXWhatsAppFacebook

Se você quer aprender Kafka de verdade…

👉 precisa simular problema real.

Aqui você vai montar um mini sistema de pedidos com:

  • produção de eventos

  • falhas controladas

  • retry automático

  • DLQ

  • paralelismo com partitions


1. Cenário do projeto (caso de uso real)

Vamos simular um e-commerce simples:

Order Service → envia pedido
↓
Kafka → distribui eventos
↓
Payment Service → processa pagamento
↓
Notification Service → envia confirmação

2. Estrutura dos serviços

kafka-lab/
├── order-service
├── payment-service
├── notification-service
├── docker-compose.yml

3. Subindo Kafka com Docker

version: '3'

services:

  zookeeper:
    image: confluentinc/cp-zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181

  kafka:
    image: confluentinc/cp-kafka
    depends_on:
      - zookeeper
    ports:
      - "9092:9092"
    environment:
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092

4. Configuração base (Spring Boot)

application.yml

spring:
  kafka:
    bootstrap-servers: localhost:9092
    consumer:
      group-id: order-group
      auto-offset-reset: earliest
    producer:
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer

5. Criando o modelo de evento

public record OrderEvent(
    String orderId,
    double value,
    String status
) {}

6. Producer (Order Service)

@Service
@RequiredArgsConstructor
public class OrderProducer {

    private final KafkaTemplate<String, Object> kafkaTemplate;

    public void sendOrder(OrderEvent event) {
        kafkaTemplate.send("order-topic", event.orderId(), event);
    }
}

7. Consumer (Payment Service)

Agora começa o interessante.

@KafkaListener(topics = "order-topic", groupId = "payment-group")
public void process(OrderEvent event) {

    if (event.value() < 0) {
        throw new RuntimeException("Pagamento inválido");
    }

    System.out.println("Pagamento processado: " + event.orderId());
}

8. Implementando Retry (automático)

Configuração avançada

@Bean
public ConcurrentKafkaListenerContainerFactory<String, Object> kafkaListenerContainerFactory(
        ConsumerFactory<String, Object> consumerFactory,
        KafkaTemplate<Object, Object> template) {

    var factory = new ConcurrentKafkaListenerContainerFactory<String, Object>();
    factory.setConsumerFactory(consumerFactory);

    var recoverer = new DeadLetterPublishingRecoverer(template);

    var errorHandler = new DefaultErrorHandler(
            recoverer,
            new FixedBackOff(2000L, 3)
    );

    factory.setCommonErrorHandler(errorHandler);

    return factory;
}

👉 Resultado:

  • tenta 3 vezes

  • espera 2s entre tentativas


9. Dead Letter Queue (DLQ)

👉 Quando falhar após retry → vai para DLQ

Nome padrão:

order-topic.DLT

Consumer da DLQ (Notification Service)

@KafkaListener(topics = "order-topic.DLT")
public void handleFailure(OrderEvent event) {
    System.out.println("Falha definitiva: " + event.orderId());
}

10. Partitions (escala real)

Criar tópico com partitions:

kafka-topics.sh --create \
  --topic order-topic \
  --partitions 3 \
  --replication-factor 1 \
  --bootstrap-server localhost:9092

👉 Agora você tem paralelismo real.


11. Consumidor concorrente

@KafkaListener(
  topics = "order-topic",
  groupId = "payment-group",
  concurrency = "3"
)

👉 Cada thread pega uma partition.


12. Garantindo ordem (chave)

kafkaTemplate.send("order-topic", event.orderId(), event);

👉 Garante ordem por pedido.


13. Idempotência (obrigatório)

if (repository.existsByEventId(event.orderId())) {
    return;
}

👉 Evita duplicação.


14. Teste prático (laboratório)

Criar endpoint no Order Service:

@PostMapping("/order")
public void createOrder(@RequestBody OrderEvent event) {
    producer.sendOrder(event);
}

Testes:

Caso normal

{
  "orderId": "1",
  "value": 100
}

👉 Vai processar normalmente


Caso erro (ativa retry + DLQ)

{
  "orderId": "2",
  "value": -10
}

Vai:

  • tentar 3 vezes

  • falhar

  • cair na DLQ


15. Fluxo completo

Order Service
   ↓
Kafka (order-topic)
   ↓
Payment Service
   ↓ (erro)
Retry (3x)
   ↓
DLQ (order-topic.DLT)
   ↓
Notification Service

16. Boas práticas (nível produção)

Sempre usar DLQ

Sempre usar retry controlado

Sempre usar chave

Idempotência obrigatória

Monitorar lag do consumer


Erros comuns

Não usar DLQ

Não tratar erro

Não pensar em concorrência

Não usar partitions

Não testar falha


Conclusão

Kafka não é só fila.

É sistema distribuído.

Se usar certo:

  • escala

  • desacopla

  • aguenta carga

Se usar errado: vira bomba-relógio.

Publicidade
Publicidade

💬 Deixe seu comentário

Deixe um comentário

Comentários passam por moderação para evitar spam e manter a qualidade.

Comentários

Ainda não tem comentários.