Solução de Problemas de Desempenho do RabbitMQ: Lentidão e Alto Uso de CPU

Diagnostique lentidão e alto uso de CPU no RabbitMQ verificando filas, consumidores, rotatividade de conexões, I/O de disco, controle de fluxo e comportamento do cliente.

Solução de Problemas de Desempenho do RabbitMQ: Lentidão e Alto Uso de CPU

O RabbitMQ é um broker de mensagens robusto e amplamente adotado, mas como qualquer sistema distribuído, pode sofrer degradação de desempenho, muitas vezes manifestada como lentidão geral ou uso excessivo de CPU. Identificar a causa raiz—seja na configuração de rede, I/O de disco ou lógica da aplicação—é crucial para manter a saúde do sistema e baixa latência.

Este guia serve como um manual prático de solução de problemas para diagnosticar e resolver gargalos comuns de desempenho em sua implantação do RabbitMQ. Examinaremos pontos críticos de monitoramento e forneceremos etapas acionáveis para otimizar a taxa de transferência e estabilizar a carga da CPU, garantindo que seu broker de mensagens tenha um desempenho confiável sob pressão.

Triagem Inicial: Identificando o Gargalo

Antes de mergulhar em mudanças profundas de configuração, é essencial identificar onde o gargalo está ocorrendo. Alto uso de CPU ou lentidão geralmente apontam para uma de três áreas: saturação de rede, I/O intensivo de disco ou interações ineficientes da aplicação com o broker.

1. Monitorando a Saúde do RabbitMQ

O primeiro passo é utilizar as ferramentas de monitoramento integradas do RabbitMQ, principalmente o Plugin de Gerenciamento.

Métricas Chave para Observar:

  • Taxas de Mensagens: Procure por picos repentinos nas taxas de publicação ou entrega que excedam a capacidade sustentada do sistema.
  • Tamanhos de Fila: Filas crescendo rapidamente indicam que os consumidores estão ficando para trás dos produtores, muitas vezes levando ao aumento da pressão sobre memória/disco.
  • Atividade de Canais/Conexões: Alta rotatividade (abertura e fechamento frequentes de conexões/canais) consome recursos significativos de CPU.
  • Alertas de Disco: Se a utilização do disco se aproximar do limite configurado, o RabbitMQ diminui deliberadamente a entrega de mensagens para evitar perda de dados (controle de fluxo).

2. Inspecionando o Sistema Operacional

O RabbitMQ é executado na VM Erlang, que é sensível à contenção de recursos em nível de SO. Use ferramentas padrão para confirmar a saúde do sistema:

  • Uso de CPU: Use top ou htop. O processo rabbitmq-server está consumindo a maior parte da CPU? Se sim, investigue a divisão do processo Erlang (veja a seção abaixo).
  • I/O Wait: Use iostat ou iotop. Tempos altos de I/O wait geralmente apontam para discos lentos, especialmente se a persistência for muito utilizada.
  • Latência de Rede: Use ping entre produtores, consumidores e os nós do broker para descartar instabilidade geral da rede.

Análise Aprofundada: Análise de Alto Uso de CPU

O alto uso de CPU no RabbitMQ é frequentemente atribuído a operações intensivas tratadas pela VM Erlang ou atividade de protocolo específica.

Entendendo a Carga do Processo Erlang

O runtime Erlang gerencia processos de forma eficiente, mas certas tarefas são intensivas em CPU. Se o uso de CPU do servidor RabbitMQ estiver em 100% em todos os núcleos, examine qual grupo de processos Erlang é o responsável.

Manipuladores de Protocolo (AMQP/MQTT/STOMP)

Se muitos clientes estão constantemente estabelecendo e derrubando conexões ou publicando grandes volumes de mensagens pequenas, o custo de CPU de autenticação, configuração de canal e manipulação de pacotes aumenta significativamente. A rotatividade frequente de conexões é um grande consumidor de CPU.

Melhor Prática: Prefira conexões persistentes e de longa duração. Use pool de conexões no lado do cliente para minimizar a sobrecarga de handshakes repetidos e fases de configuração.

Indexação de Filas e Mensagens Persistentes

Quando as filas são altamente utilizadas, especialmente quando as mensagens são persistentes (gravadas em disco), a carga da CPU pode aumentar devido a:

  1. Gerenciamento de I/O de Disco: Coordenação de gravações em disco e liberação de buffers.
  2. Indexação de Mensagens: Manter o controle das localizações das mensagens dentro da estrutura da fila, particularmente em filas altamente duráveis e de alta taxa de transferência.

Limitação e Controle de Fluxo

O RabbitMQ implementa controle de fluxo para se proteger quando os recursos estão restritos. Se um nó atingir um limite máximo de memória ou espaço em disco, ele aplica limitação interna, que pode se manifestar como lentidão para os produtores.

Se você vir inúmeras mensagens bloqueadas devido ao controle de fluxo, a solução imediata é liberar recursos (por exemplo, garantir que os consumidores estejam ativos ou aumentar o espaço em disco). A correção de longo prazo é escalar o cluster ou otimizar a taxa de transferência do consumidor.

Solucionando Problemas de Consumidores Lentos e Acúmulo de Filas

A lentidão é frequentemente percebida pela camada de aplicação quando os consumidores não conseguem acompanhar a taxa de entrada. Isso geralmente é um problema do lado do consumidor ou um problema de rede entre o consumidor e o broker.

Estratégia de Confirmação do Consumidor

Como os consumidores confirmam as mensagens impacta profundamente a taxa de transferência e o uso de CPU no broker.

  • Confirmação Manual (manual ack): Fornece confiabilidade, mas requer que o consumidor confirme o recebimento. Se o consumidor travar, o RabbitMQ retém a mensagem, potencialmente acumulando memória e causando atrasos para outras mensagens nessa fila.
  • Confirmação Automática (auto ack): Maximiza a taxa de transferência inicialmente, mas se o consumidor falhar após receber uma mensagem, mas antes de processá-la, a mensagem é perdida para sempre.

Se você estiver usando confirmações manuais e observando lentidão, verifique a contagem de Mensagens Não Confirmadas no Plugin de Gerenciamento. Se esse número for alto, os consumidores estão lentos ou falhando em confirmar.

Otimização da Contagem de Prefetch

A configuração qos (Qualidade de Serviço), especificamente a contagem de prefetch, determina quantas mensagens um consumidor pode manter não confirmadas.

Se a contagem de prefetch for definida muito alta (por exemplo, 1000), um único consumidor lento pode puxar um grande backlog da fila, privando outros consumidores potencialmente mais rápidos na mesma fila.

Exemplo: Se um consumidor está processando apenas 10 msg/seg, definir prefetch_count como 100 é um desperdício e concentra carga desnecessariamente.

# Exemplo de definição de uma contagem de prefetch razoável (ex.: 50)
# Usando uma biblioteca cliente equivalente (Representação conceitual)
channel.basic_qos(prefetch_count=50)

Latência de Rede Entre Consumidor e Broker

Se o consumidor for rápido, mas demorar muito para confirmar as mensagens recebidas pela rede, o problema é provavelmente latência ou saturação de rede entre o consumidor e o nó RabbitMQ ao qual está conectado.

  • Teste: Conecte temporariamente o consumidor ao broker na mesma máquina (localhost) para eliminar variáveis de rede. Se o desempenho melhorar drasticamente, concentre-se na otimização da rede (por exemplo, NICs dedicadas, verificação de firewalls intermediários).

Impacto de I/O de Disco e Persistência

O desempenho do disco é frequentemente o teto máximo de desempenho, particularmente para filas que utilizam alta durabilidade.

Mensagens Persistentes e Durabilidade

  • Exchanges e Filas Duráveis: Essenciais para evitar perda na reinicialização do broker, mas incorrem em sobrecarga de metadados.
  • Mensagens Persistentes: Mensagens sinalizadas como persistentes devem ser gravadas em disco antes que o broker envie uma confirmação de volta ao produtor. Discos lentos se traduzem diretamente em baixa taxa de transferência do produtor.

Se sua carga consiste principalmente de mensagens transitórias (não persistentes), certifique-se de que a própria fila não seja durável ou, mais praticamente, marque as mensagens como transitórias se a perda de dados for aceitável para esse payload específico. Mensagens transitórias são muito mais rápidas, pois permanecem na RAM (sujeitas à pressão de memória).

Sobrecarga de Espelhamento

Em um cluster de alta disponibilidade (HA), o espelhamento de filas replica dados entre nós. Embora essencial para tolerância a falhas, o espelhamento adiciona carga significativa de gravação ao cluster. Se a latência do disco for alta, essa carga pode saturar a capacidade de I/O, diminuindo todas as operações.

Dica de Otimização: Para filas que exigem alta taxa de transferência de gravação, mas podem tolerar perda menor de dados durante um failover (por exemplo, streams de log), considere usar filas não espelhadas em um conjunto de nós de alta disponibilidade ou use Filas Preguiçosas se o comprimento da fila for esperado para se tornar extremamente grande (Filas Preguiçosas movem mensagens não consumidas para o disco mais cedo para economizar RAM).

Separe Problemas do Broker de Problemas da Aplicação

O RabbitMQ é frequentemente culpado pela latência que começa em outro lugar. Uma solicitação web expira, um trabalho termina tarde ou um banco de dados downstream está lento, e a fila é a coisa mais fácil de notar porque sua profundidade é visível. Antes de ajustar o broker, decida se o RabbitMQ está lento ou se o RabbitMQ está mostrando que os consumidores estão lentos.

Comece com três números para a fila afetada:

rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers \
  message_stats.publish_details.rate message_stats.deliver_get_details.rate \
  message_stats.ack_details.rate

Se messages_ready cresce enquanto os consumidores estão presentes e as confirmações são lentas, os consumidores não estão acompanhando. O broker pode estar saudável. Se messages_unacknowledged cresce, os consumidores estão recebendo mensagens, mas não as finalizando ou confirmando. Se as confirmações de publicação se tornam lentas enquanto os alertas de disco ou memória estão ativos, o broker está aplicando contrapressão. Se a CPU está alta e as contagens de conexão estão subindo, o comportamento do cliente pode ser a causa.

Essa distinção é importante porque adicionar RAM ao RabbitMQ não corrigirá um consumidor que gasta dois segundos chamando uma API lenta para cada mensagem. Aumentar as réplicas do consumidor não corrigirá um broker que está limitado por gravações em disco. Alterar o prefetch não corrigirá um produtor que abre uma nova conexão TCP para cada publicação.

Rotatividade de Conexões e Canais

Alto uso de CPU do RabbitMQ é muitas vezes entediante: muitos clientes estão repetidamente abrindo e fechando conexões. A configuração de uma conexão AMQP não é gratuita. Inclui configuração TCP, negociação TLS opcional, autenticação, ajuste e negociação de canal. Se uma aplicação abre uma conexão para cada mensagem, cada solicitação HTTP ou cada trabalho curto, o RabbitMQ gasta CPU em trabalho de configuração em vez de mover mensagens.

Verifique a idade e as contagens das conexões:

rabbitmqctl list_connections name user peer_host state channels connected_at
rabbitmqctl list_channels connection number user vhost

Se você vir um fluxo constante de conexões de curta duração do mesmo serviço, corrija o cliente. Mantenha as conexões de longa duração. Use os canais adequadamente. A maioria dos serviços deve criar uma conexão durante a inicialização e reutilizá-la até o desligamento, com lógica de reconexão para falhas. Em aplicações web, não crie uma conexão com o broker dentro do manipulador de solicitação, a menos que seu framework tenha um pool de conexões muito deliberado.

O TLS torna a rotatividade mais cara. O TLS é bom para produção, mas handshakes repetidos podem se tornar visíveis sob carga. Reutilizar conexões ainda é a correção.

Prefetch que Corresponde ao Trabalho

Prefetch não é um botão mágico de taxa de transferência. Ele controla quantas mensagens não confirmadas um consumidor pode manter. O valor certo depende do tempo de processamento, tamanho da mensagem e justiça entre consumidores.

Um prefetch de 1 é simples e justo, mas pode subutilizar consumidores quando cada trabalho tem pequenas esperas por rede ou disco. Um prefetch de 500 pode parecer rápido em um benchmark, mas um consumidor lento pode acumular trabalho e aumentar a dor da redistribuição quando falha.

Um ponto de partida prático é medir quanto tempo um consumidor gasta por mensagem. Se o trabalho é intensivo em CPU e cada processo lida com uma mensagem por vez, mantenha o prefetch baixo. Se o trabalho espera em um serviço remoto e o consumidor lida com concorrência internamente, um prefetch moderado pode mantê-lo ocupado. Aumente em etapas e observe:

  • taxa de confirmação;
  • messages_unacknowledged;
  • memória do consumidor;
  • latência ponta a ponta;
  • contagem de redistribuição após uma reinicialização do consumidor.

O teste deve incluir falha. Mate um consumidor enquanto ele mantém mensagens não confirmadas. Se a redistribuição causar uma enorme explosão de trabalho duplicado ou longas paradas, o prefetch provavelmente está muito alto para essa fila.

Mensagens Persistentes e Realidade do Disco

Mensagens persistentes e filas duráveis são a escolha certa para trabalhos importantes, mas movem parte do gargalo para o armazenamento. Quando os publicadores esperam por confirmações, gravações lentas em disco aparecem como publicação lenta. Quando as filas crescem muito, o RabbitMQ tem mais trabalho de indexação e armazenamento a fazer. Em configurações em cluster, a replicação adiciona trabalho de rede e disco também.

Verifique os sintomas do disco a partir do sistema operacional:

iostat -xz 1
vmstat 1

Alto I/O wait, alta utilização do disco ou longos tempos de espera indicam que o broker está esperando pelo armazenamento. Isso não significa "desligar a persistência". Significa que você precisa de armazenamento mais rápido, menos mensagens persistentes desnecessárias, menor taxa de publicação, loteamento mais eficiente ou uma topologia que distribua o trabalho entre os nós.

Evite colocar diretórios de dados do RabbitMQ em discos de rede lentos, a menos que você tenha testado a configuração exata. O RabbitMQ se preocupa tanto com a latência quanto com a taxa de transferência. Um disco que parece aceitável para cópias de arquivos em massa ainda pode ser ruim para cargas de trabalho de mensagens.

Tipo de Fila e Escolhas de Replicação

Orientações mais antigas do RabbitMQ frequentemente mencionam filas clássicas espelhadas. Em implantações atuais do RabbitMQ, as filas de quorum são comumente preferidas para cargas de trabalho duráveis replicadas, enquanto as filas clássicas ainda se encaixam em muitos casos não replicados ou menos críticos. A melhor escolha depende da versão do RabbitMQ, requisitos operacionais e carga de trabalho.

As filas de quorum melhoram o modelo de falha para filas duráveis replicadas, mas não são gratuitas. Elas replicam através de um protocolo de consenso, então as gravações envolvem vários nós. Se você colocar cada stream de evento transitório de alto volume em filas de quorum, pode criar um problema de desempenho que não precisava.

Use durabilidade mais forte onde corresponder ao valor do negócio:

  • fluxos de trabalho de pagamento, pedido, inventário e auditoria geralmente merecem filas duráveis replicadas;
  • atualização de cache, métricas e notificações recriáveis podem não precisar da mesma proteção;
  • backlogs muito grandes podem precisar de uma revisão de design em vez de apenas um broker maior.

O ponto não é minimizar a segurança. É evitar pagar o maior custo de confiabilidade por dados que podem ser recriados, enquanto ainda protege mensagens que não podem.

Mensagens Grandes Tornam Tudo Mais Difícil

O RabbitMQ pode transportar mensagens grandes, mas as filas geralmente são mais saudáveis quando as mensagens são pequenas. Uma mensagem que contém uma imagem grande, relatório, arquivo ou exportação completa de banco de dados aumenta a pressão de memória, pressão de disco, tempo de transferência de rede e custo de redistribuição.

Para payloads grandes, armazene o payload em armazenamento de objetos ou um banco de dados e envie uma mensagem contendo uma referência:

{
  "job_id": "report-2026-05-25-001",
  "object_url": "s3://reports-bucket/report-2026-05-25-001.json",
  "sha256": "..."
}

O consumidor busca o payload quando está pronto para processar. Este design não é perfeito; agora você precisa de limpeza do ciclo de vida e controle de acesso para o armazenamento de payload. Mas mantém o RabbitMQ focado na coordenação em vez de se tornar um sistema de transporte de arquivos.

Quando a CPU Está Alta, mas as Filas Estão Vazias

Filas vazias nem sempre significam que o RabbitMQ está ocioso. A CPU pode estar alta porque os clientes estão constantemente se conectando, autenticando, publicando mensagens não roteáveis, declarando topologia ou pesquisando com padrões ineficientes.

Verifique a UI de gerenciamento ou CLI para rotatividade de conexão e contagens de canais. Revise os logs da aplicação para loops de reconexão. Procure por clientes que declaram exchanges e filas antes de cada publicação. A declaração de topologia é geralmente idempotente, mas fazê-la em frequência muito alta ainda adiciona trabalho ao broker.

Verifique também os plugins. Gerenciamento, federação, shovel, MQTT, STOMP, rastreamento e plugins personalizados adicionam trabalho quando habilitados e usados. Não desabilite um plugin cegamente durante um incidente, mas confirme se a carga está alinhada com a atividade do plugin.

Uma Rotina de Ajuste Mais Segura

Altere uma coisa de cada vez e registre os números antes/depois. O trabalho de desempenho do RabbitMQ fica confuso quando prefetch, contagem de consumidores, tipo de fila, persistência e hardware mudam todos na mesma implantação.

Uma rotina útil:

  1. Capture taxas de fila, profundidade da fila, mensagens não confirmadas, contagem de conexões, CPU, memória, I/O de disco e latência de confirmação de publicação.
  2. Escolha o gargalo provável.
  3. Faça uma alteração.
  4. Execute a mesma carga de trabalho.
  5. Compare a latência ponta a ponta, não apenas a taxa de transferência do broker.

Se o incidente estiver ativo, escolha mudanças reversíveis primeiro: adicione consumidores, pare a rotatividade de conexão, reduza a taxa do produtor, drene um backlog ou mova cargas de trabalho opcionais para longe. Guarde migrações de tipo de fila e redesenho de armazenamento para trabalho planejado, a menos que o sistema já esteja inativo e você não tenha um caminho mais seguro.

Resumo das Etapas Acionáveis

Ao enfrentar alto uso de CPU ou lentidão generalizada, siga esta lista de verificação:

  1. Verifique Alertas: Confirme se nenhum alerta de controle de fluxo de disco ou memória está ativo.
  2. Inspecione o Comportamento do Cliente: Procure por alta rotatividade de conexão/canal ou clientes usando auto-ack inapropriadamente.
  3. Otimize Consumidores: Ajuste prefetch_count para corresponder à velocidade real de processamento de seus consumidores.
  4. Verifique a Velocidade do Disco: Certifique-se de que o backend de armazenamento seja rápido o suficiente para seus requisitos de persistência e replicação.
  5. Perfile o Erlang (Avançado): Use ferramentas Erlang (ex.: observer) para confirmar se a CPU é gasta em manipulação de protocolo versus gerenciamento interno de filas.

Ao analisar sistematicamente a utilização de recursos nos níveis de SO, broker e aplicação, você pode isolar e eliminar efetivamente as causas raiz dos problemas de desempenho do RabbitMQ.