Voltar para o Blog

Como Reduzimos a Perda de Dados de 13% para 4% Usando Arquitetura Orientada a Eventos com RabbitMQ

Em sistemas distribuídos modernos, a integridade dos dados é inegociável. Ao atuar em soluções globais que abrangem múltiplos países, depender puramente de modelos de requisição-resposta síncronos sob instabilidade de rede é a receita para falhas silenciosas e perda de receita.

Neste artigo, detalho um caso real em que enfrentamos uma taxa de perda de dados crítica de 13% na ingestão de métricas e KPIs globais de vendas, e como resolvemos isso sistematicamente ao fazer a transição para uma arquitetura orientada a eventos assíncrona usando RabbitMQ.


O Cenário e a Dor Crítica de Negócio

Gerenciávamos as métricas de performance e KPIs de vendedores em larga escala global. A aplicação original dependia de chamadas HTTP síncronas tradicionais entre os microsserviços para atualizar transações e consolidar pontuações de vendas.

Sob circunstâncias ideais, isso funcionava. No entanto, em regiões com infraestrutura de rede heterogênea e quedas transitórias de conectividade, as requisições síncronas sofriam com:

  1. Instabilidade de Rede: Timeouts de rede faziam as requisições falharem no meio do caminho, deixando os dados em estado inconsistente.
  2. Acoplamento Extremo: Se o microsserviço de destino estivesse temporariamente sobrecarregado ou indisponível, a API de origem falhava instantaneamente, resultando na perda imediata do dado da transação.
  3. Esgotamento de Threads: Chamadas síncronas bloqueavam as threads de processamento nas nossas APIs, gerando lentidão em cascata e degradação geral do sistema.

O impacto de negócio? Cerca de 13% de todas as atualizações eram perdidas, gerando métricas de KPIs incorretas. Isso impactava diretamente o cálculo de bônus dos vendedores e a confiabilidade da plataforma.


A Solução: Projetando o Escudo de Eventos com RabbitMQ

Para solucionar isso de forma definitiva, desacoplamos o fluxo de ingestão, migrando de APIs síncronas bloqueantes para um Loop de Eventos Assíncronos com o RabbitMQ atuando como um buffer altamente resiliente.

Abaixo está o fluxo da nova arquitetura:

[Evento de Transação] ──> [API Publicadora] ──> [RabbitMQ Exchange]
                                                       │
                                                       ▼
                                             [Fila Persistente]
                                                       │
                                                       ▼
                                           [Consumidor Java] ──> [Banco de Dados]

Ao introduzir o RabbitMQ:

  • A API Publicadora simplesmente emite um evento leve para o Exchange do RabbitMQ e retorna imediatamente o status 202 Accepted para o cliente, liberando a requisição do usuário de forma quase instantânea.
  • O RabbitMQ atua como amortecedor de choque, garantindo que o evento seja gravado em disco (mensagens persistentes) e armazenado de forma segura nas filas.
  • O Consumidor Java processa as mensagens de forma assíncrona. Se o banco de dados de destino estiver lento ou sofrendo carga, o consumidor naturalmente reduz o ritmo de processamento (controle de backpressure), sem perder uma única mensagem.

Implementando Resiliência em Java & Spring Boot

Para tornar essa arquitetura verdadeiramente robusta, precisamos configurar o produtor e o consumidor com padrões de alta disponibilidade do ecossistema AMQP/Spring.

1. Configuração do Publicador Resiliente (Producer)

O produtor precisa garantir que a mensagem chegou com segurança ao exchange do RabbitMQ:

@Configuration
public class RabbitConfig {

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        
        // Habilita as confirmações de publicação (Publisher Confirms)
        template.setConfirmCallback((correlationData, ack, cause) -> {
            if (!ack) {
                log.error("Falha ao entregar mensagem no Exchange. Motivo: {}", cause);
                // Trata falha em banco de contingência ou gera alerta
            }
        });
        
        return template;
    }
}

2. Consumidor Idempotente e Tolerante a Falhas

Os consumidores precisam tratar quedas de banco de dados e instabilidades de rede usando confirmações manuais explícitas (Manual Acks):

@Component
public class TransactionKpiConsumer {

    @RabbitListener(queues = "seller.kpi.queue", ackMode = "MANUAL")
    public void consumeTransactionEvent(TransactionEvent event, Channel channel, 
                                        @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
        try {
            // 1. Processa e agrega as métricas de performance
            kpiService.processAndAggregate(event);
            
            // 2. Confirma a mensagem no RabbitMQ somente após sucesso no banco de dados
            channel.basicAck(tag, false);
            
        } catch (TransientDataAccessException e) {
            log.warn("Erro temporário no banco de dados. Devolvendo mensagem para a fila.");
            // Re-enfileira a mensagem para ser processada novamente após a instabilidade
            channel.basicNack(tag, false, true);
            
        } catch (Exception e) {
            log.error("Erro fatal de parsing do payload. Enviando para a DLQ (Dead Letter Queue).");
            // Rejeita a mensagem sem re-enfileirar para evitar loops infinitos ("poison pill")
            channel.basicReject(tag, false);
        }
    }
}

O Impacto de Negócio e de Arquitetura

Essa mudança transformou radicalmente a estabilidade operacional e a integridade de dados da nossa plataforma:

  • Redução Crítica na Perda de Dados: A taxa de perda de dados despencou de 13% para menos de 4%, garantindo que a apuração de KPIs fosse 100% confiável.
  • Controle de Carga Inteligente (Backpressure): Nos horários de pico comercial, o RabbitMQ amortecia rajadas massivas de eventos, permitindo que a infraestrutura backend consumisse as transações de forma gradual e controlada, sem estressar os bancos de dados.
  • Tempo de Resposta Reduzido: A API de transações na ponta upstream reduziu seu tempo de resposta médio em mais de 70%, pois não precisava mais aguardar a execução de lógicas de agregação complexas.

Lição Principal para Arquitetos

Para engenheiros sêniores e arquitetos de software, o aprendizado deste caso é vital: nunca deixe gargalos síncronos limitarem a performance de sistemas distribuídos. Migrar para arquiteturas orientadas a eventos não apenas protege os sistemas contra falhas de infraestrutura e oscilações de rede, mas também protege diretamente o faturamento e a credibilidade do negócio.