Este documento discute como mensagens podem ser perdidas no RabbitMQ em três frases ou menos:
1) Mensagens podem ser perdidas se o consumidor confirmar o recebimento antes de processá-las e falhar antes de completar o processamento.
2) Mensagens podem expirar e ser removidas se seu tempo de vida (TTL) for atingido antes de serem processadas.
3) Mensagens armazenadas apenas na memória podem ser perdidas durante falhas no broker.
5. Conceitos Básicos - Connection
3
• Todos protocolos suportados pelo RabbitMQ são baseados em TCP/IP com conexões
duráveis (long-lived connections)
• Cada conexão de cliente com o broker utiliza uma única conexão TCP na porta
destinada ao protocolo específico que estiver sendo utilizado.
• Após conectar-se ao broker, os clientes podem publicar e consumir mensagens,
definir a topologia (queues, exchanges, bindings) e utilizar as operações suportadas por
cada protocolo.
• Como a conexão permanece ativa, as mensagens são entregues de forma push-
based, ou seja, não é necessário realizar polling, elas são enviadas de forma reativa
6. Conceitos Básicos - Channel
• Podem ser vistos como conexões lógicas leves que são
úteis quando uma mesma aplicação deseja ter múltiplos
canais isolados de comunicação com o broker
• Permite o compartilhamento de uma mesma conexão
TCP/IP com múltiplas conexões lógicas ao broker.
• Evita o uso excessivo de recursos de hardware
• Simplifica questões como liberação de portas em firewall.
• Se baseia em um channel number que é um número
identificador compartilhado entre o cliente e o broker.
• São excluídos quando a conexão é encerrada
• Permite o isolamento do tratamento de erros e métricas
4
7. Conceitos Básicos - Queue
• Estrutura sequencial de dados, que permite realizar as ações de publicação (enqueue) e
consumo (dequeue) de mensagens entre produtores e consumidores. Funciona como um
grande buffer, limitado à quantidade de memória e disco disponíveis.
• Podem ser classificadas quanto à persistência:
• Duráveis: persistem mesmo após o reinício do broker
• Transientes: são apagadas uma vez que o broker é reiniciado
• Filas também podem ser criadas como “Exclusivas”, o que significa que poderão ser
utilizadas por apenas uma conexão, e são excluídas quando essa conexão é encerrada.
5
8. Conceitos Básicos - Message
• São as mensagens enviadas ao broker, que podem ser classificadas quanto ao tipo
de entrega (Delivery Mode):
6
• Não Persistentes: são escritas no
disco somente quando o espaço em
memória estiver sob pressão
(memory pressure)
• Persistentes: são escritas no
disco assim que elas chegam à
fila, porém, são mantidas em
memória também na medida do
possível.
É possível configurar as operações de liberação de memória de uma fila com o parâmetro:
lazy_queue_explicit_gc_run_operation_threshold
(quanto mais alto, maior performance, porém, maior o uso de memória)
9. Conceitos Básicos - Exchange
• Mecanismo de roteamento de mensagens provido pelo RabbitMQ
• Permitem implementar diversos Enterprise Integration Patterns
• Permitem multiplexar uma mesma mensagem para várias filas, o que pode ser
útil quando queremos trabalhar com comunicação multicast
7
Fonte das imagens:
https://www.rabbitmq.com/tutorials/amqp-concepts.html
10. Conceitos Básicos - Exchange - Enterprise Integration Patterns
8
Fonte das imagens:
https://www.enterpriseintegrationpatterns.com/patterns/messaging/
https://camel.apache.org/components/latest/eips/message-router.html
https://camel.apache.org/components/latest/eips/publish-subscribe-channel.html
https://camel.apache.org/components/latest/eips/content-based-router-eip.html
• Message Router
• Publish Subscribe
• Content Based Router
Em algumas situações, por exemplo, quando uma mensagem
não pode ser roteada, ela pode ser retornada para o produtor,
descartada, ou, se o broker implementar uma extensão
específica, ser enviada para uma "dead letter queue". O produtor
determina esse comportamento definindo propriedades
específicas nas mensagens enviadas.
funciona como um filtro que roteia de um canal para outro
baseado em alguma condição - por ex.: uma routing key roteia de um canal para outro baseado no conteúdo da mensagem
roteia de um canal para N outros baseado em um
canal/tópico de interesse
11. Conceitos Básicos - Exchange - Tipos e Aplicações
9
Tipo de Exchange Descrição
Default Por padrão, toda fila é vinculada a esse exchange, onde a
Routing Key utilizada é o próprio nome da fila. Interessante para
a troca de mensagens simples entre produtor e consumidor.
Direct Entrega mensagens para uma fila baseado em uma routing key.
Interessante para implementar o padrão Message Router.
Fanout Entrega as mensagens para todas as filas que estiverem
vinculadas a ele, ignorando as routing keys. Interessante para
implementar comunicação anycast.
• Os tipos de algoritmos de roteamento usados dependem do tipo de Exchange e das regras
configuradas, também chamadas de bindings.
12. Conceitos Básicos - Exchange - Tipos e Aplicações
10
Tipo de Exchange Descrição
Topic Entrega as mensagens para uma ou mais filas
baseando-se na routing key e no padrão que foi
utilizado para vincular a fila ao exchange. Interessante
para o padrão publish/subscribe.
Headers Desenhado para rotear mensagens baseado em
múltiplos atributos que são melhores expressos como
cabeçalhos de mensagens do que uma única routing
key. Interessante para implementar o padrão Content
Based Router.
14. Protocolos
12
Há diversos protocolos suportados e que podem ser habilitados como plugins. O protocolo
mais comumente utilizado é o AMQP versões 0-9-*.
• AMQP 0-9-1 + extensões - é a versão mais recente do protocolo “core” suportado pelo
broker, porém, possui diversas extensões como, por exemplo:
• Controle de TTL a nível de fila ou de mensagem
• Dead letter queue - que permite rotear uma mensagem para outros tópicos caso elas
sejam expiradas ou rejeitadas
• Limite de tamanho de filas
• AMQP 1.0 - versão difere bastante do AMQP 0-9-1, possui diversas limitações, e menos
clients disponíveis. No entanto, é mais simples de adicionar suporte a ela em diferentes
brokers.
• O plugin não suporta, por exemplo, entrega do tipo Exacly Once
15. Protocolos
13
• STOMP - Simple Text Oriented Messaging Protocol é um protocolo simples baseado em
texto, pode ser usado, inclusive, direto via telnet.
• Suporta utilização de um adaptador para Websockets
• Por ser muito simples, possui grande variedade de clients
• https://stomp.github.io/implementations.html
• MQTT - protocolo binário leve, destinado à comunicação pub/sub.
• Se baseia em algoritmo de consenso, ficando disponível somente se a maioria dos
nós do cluster estiver online.
• Suporta armazenamento em memória ou disco
• Suporta utilização de um adaptador para WebSockets
17. Cenários de possível perda de mensagens - AutoAck
15
• É possível configurar os consumidores para enviarem um sinal de Acknowledgement
automaticamente, logo após realizar a leitura da mensagem.
• Esse mecanismo traz certa comodidade de implementação quando não precisamos nos
preocupar se o processamento da mensagem foi finalizado. O desenho abaixo ilustra um
cenário de sucesso com essa configuração.
1. Produtor envia M1 para o broker
2. Consumidor recebe M1 e responde com o AutoAck
3. Consumidor processa com sucesso M1
4. Broker exclui a mensagem (concorrente com 3)
18. Cenários de possível perda de mensagens - AutoAck
16
• O ponto de atenção é que, consumidores podem falhar logo após enviar o ACK para o
broker, o que pode ser indesejado.
• O desenho abaixo ilustra um cenário de perda de mensagem utilizando AutoAck.
1. Produtor envia M1 para o broker
2. Consumidor recebe M1 e responde com o AutoAck
3. Consumidor falha e NÃO processa M1
4. Broker exclui a mensagem (concorrente com 3)
Obs.: Como o consumidor confirmou o recebimento da
mensagem, o broker não se preocupará em entregá-la
novamente
19. Cenários de possível perda de mensagens - AutoAck
17
• A outra forma de implementar é realizando o envio manual do sinal de Acknowledgement.
• Com isso, em caso de falha, o broker tentará entregar a mensagem novamente
1. Produtor envia M1 para o broker
2. Consumidor recebe M1, falha e NÃO processa M1
3. Broker reenvia M1
4. Consumidor recebe M1 pela segunda vez e processa
M1
5. Consumidor envia ACK de M1
6. Broker exclui a mensagem (somente após 4)
Obs.: Se o processamento do consumidor não for
idempotente, é possível gerar inconsistências, por
exemplo, um pagamento realizado em duplicidade
20. Cenários de possível perda de mensagens - TTL - Time-to-live
18
• Dependendo do protocolo utilizado, existe suporte para a definição de um tempo máximo
de vida da mensagem na fila (TTL).
• Não há um valor default, pois, a fila pode ser criada sem essa policy.
• Essa configuração pode ser realizada para uma mensagem específica, ou para uma fila
inteira.
• Caso não seja configurada uma DLQ (Dead Letter Queue), as mensagens que não forem
lidas a tempo pelos consumidores poderão ser perdidas. Em contrapartida, se o TTL for
muito grande, pode não haver recursos disponíveis (Memória e Disco) para armazenar as
mensagens.
1. Produtor envia M1
2. Produtor envia M2
3. Produtor envia M3
4. Produtor envia M4
5. M1 atinge seu TTL e é excluída da fila
6. Consumidor inicia a leitura de mensagens
7. Consumidor lê M2, pois M1 já foi excluída
da fila
21. Cenários de possível perda de mensagens - TTL - Time-to-live
19
• O desenho abaixo ilustra uma possível alternativa utilizando uma Dead Letter Queue.
• A configuração é realizada adicionando uma policy à fila
1. Produtor envia M1
2. Produtor envia M2
3. Produtor envia M3
4. Produtor envia M4
5. M1 atinge seu TTL
6. M1 é movida para a DLQ
7. Consumidor inicia a leitura de mensagens
8. Consumidor lê M2, pois M1 já foi excluída da
fila
Obs.: M1 pode ser reprocessada futuramente e, inclusive,
ser reenviada para a fila original
args.put("x-dead-letter-exchange", "some.exchange.name");
args.put("x-dead-letter-routing-key", "some-routing-key");
22. Cenários de possível perda de mensagens – Mensagens não persistentes
20
• Quando há indisponibilidade de um ou mais nós do cluster de RabbitMQ, mensagens que
estejam na memória desses nós podem ser perdidas
1. Produtor envia M1
2. Produtor envia M2
3. Produtor envia M3
4. Produtor envia M4
5. Broker é reiniciado e as mensagens
armazenadas na fila são PERDIDAS
Obs.: É possível utilizar mecanismos de High Availability
como as Mirrored Queues ou Quorum Queues para
evitar esse tipo de perda de mensagens
24. Mecanismos de Alta Disponibilidade – Mirrored Queues
22
• Permite obter tolerância de partição, pois replica as
mensagens das filas para mais nós do cluster
• Utiliza Chained Replication Algorithm (topologia de anel -
cadeia de mirrors)
• Erlang Partition Detection é responsável pela
descoberta de novas partições.
• net_ticktime define a frequencia do heartbeat entre
os nós do cluster
• Se configurado muito lento - pode demorar para
identificar partições offline
• Muito rápido - pode dar falsos positivos em relação
aos nós que estão disponíveis
• Sincronização usa mais rede que o necessário, falhas de
partições são lentas de detectar
• Quando um broker (Nó) cai e sobe novamente, ele sobe
vazio
• Comando "queues rebalance" pode ser usado para
rebalancear as filas manualmente, ou pode ser
configurada a replicação automática
Fonte das imagens:
https://www.rabbitmq.com/blog/2020/04/20/rabbitmq-gets-an-ha-upgrade/
https://www.rabbitmq.com/nettick.html
25. Mecanismos de Alta Disponibilidade – Mirrored Queues – Sincronização
23
• Sincronização pode ser:
• Manual ou Automática
• Param o cluster para fazer a sincronização dos brokers
• Tornam o serviço indisponível momentaneamente
• Usadas quando queremos priorizar consistência no lugar de disponibilidade
• Ideal para filas pequenas, para minimizar o tempo de indisponibilidade
• Natural
• Normalmente usada para filas grandes, somente são replicadas as mensagens
novas para novos mirrors, isso pode causar a perda de mensagens antigas que
por ventura tenham ficado apenas no nó que estava previamente ativo, caso
ele venha a falhar
Abaixo temos um exemplo de policy onde as filas cujos nomes começam com "two." são
espelhadas para quaisquer 2 nós do cluster, com automatic synchronisation habilitada:
rabbitmqctl set_policy ha-two "^two."
'{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}' Fonte do comando:
https://www.rabbitmq.com/ha.html#interstitial
26. Mecanismos de Alta Disponibilidade – Quorum Queues
24
• Criadas para maior Data Safety - passarão a ser a configuração padrão de HA
• Usa Raft Consensus Algorithm – Mesmo utilizado pelo Consul, Vault
• https://raft.github.io/
• Por exemplo, um cluster de 5 servidores pode continuar operando mesmo que 2
servidores falhem (N/2+1 ativos). Se mais servidores falharem, eles pararão de
progredir (mas nunca irão retornar um resultado incorreto).
• Prioriza consistência em detrimento da disponibilidade
• Utiliza a biblioteca RA, que é mais rápida para identificar queda de nós ou rede entre
os brokers
• Introduz o conceito de Replication Factor
• É tolerante a falhas, desde que seja mantida a maioria para eleição e replicação de
mensagens
• Recomendado usar números ímpares de nós para quorum
• Quanto maior o número de réplicas, menor a performance, naturalmente
• Não suportam algumas funcionalidades das mirrored queues, como:
• TTL
• Mensagens não persistentes
• Prioridades de mensagens
27. Mecanismos de Alta Disponibilidade – Quorum Queues
25
• Dividido em 2 fases
• Eleição de líder
• Assim como nas mirror queues, é necessário eleger um nó Master/Líder ao iniciar
o cluster, ou ao perder um nó líder
• RA dispara as eleições quando identifica a queda de um nó Líder
• Biblioteca RA é mais rápida para identificar queda de nós ou rede entre
os brokers
• Replicação de log - ACKs são direcionados para o líder, e não mais para cada broker
em forma de anel
• A estrutura de dados por baixo é um log
• Replica comandos de ENQUEUE e de ACKs no command log para todos os
brokers
• Baseado em replicação de logs - uma vez que a maioria dos nós possuem a
mensagem, então ela pode ser enviada para os consumers
• A queue é apenas um snapshot instantâneo do command log, o que garante
maior segurança sobre a informação armazenada em comparação às mirrored
queues
• Conforme o broker vai recebendo os ACKS, e replicando esses ACKs para os
nós, é possível rodar a limpeza do log, removendo as mensagens que já
possuem confirmação de leitura
28. Mecanismos de Alta Disponibilidade – Quorum Queues
26
• Storage
• Write ahead log – a mensagem é escrita em disco antes de replicar para todos nós
• Quando o log se torna muito grande (acima de 500GiB) é criado um novo arquivo de WAL (Write
Ahead Log) para melhor performance
• Periodicamente, WAL é lido e os dados movidos para segment files
• WAL é usado apenas para garantir que nenhum dado será perdido - como em um banco de dados
• É possível trabalhar com um ou mais segment files por queue
• A mensagem sempre será escrita uma vez
• Potencialmente ela será escrita 2 vezes (quando for movida para um segment file)
• Comparado com Mirrored Queues – onde a mensagem pode nunca ser escrita, ou escrita
apenas 1 vez
• Que é uma das causas da limitação forte entre consistência e disponibilidade
• Para criar uma Quorum Queue, basta criá-la com o argumento x-queue-type igual a quorum
• Não é possível utilizar polices para configurar uma fila como quorum queue
30. Exemplos – Setup do Ambiente
28
• K6
• https://k6.io/docs/
• Producer Service
• https://github.com/alvsantos/rabbitmq-
producer-service
• RabbitMQ
• https://github.com/harbur/docker-
rabbitmq-cluster
• Consumer Service
• https://github.com/alvsantos/rabbitmq-
consumer-service
• Processor Service
• https://github.com/alvsantos/rabbitmq-
processor-service
AutoAck
HABILITADO
DISPONÍVEL
31. Exemplos - AutoAck Habilitado – Cenário Feliz
29
• Com o K6, Iremos simular 10 usuários
virtuais postando uma mensagem a cada
100ms, durante 30s
• Pode gerar o máximo de 3000
mensagens, porém, devido aos tempos
de resposta da API, será menos que isso
• Manteremos o Consumer Service com
AutoAck HABILITADO
• Manteremos o Processor Service
DISPONÍVEL durante todo tempo
• O resultado esperado é que 100% das
mensagens enviadas sejam computadas no
Processor Service
AutoAck
HABILITADO
DISPONÍVEL
32. Exemplos - AutoAck Habilitado – Cenário de Falha
30
• Com o K6, Iremos simular 10 usuários
virtuais postando uma mensagem a cada
100ms, durante 30s
• Pode gerar o máximo de 3000
mensagens, porém, devido aos tempos
de resposta da API, será menos que isso
• Manteremos o Consumer Service com
AutoAck HABILITADO
• O Processor Service ficará INDISPONÍVEL
por alguns instantes
• O resultado esperado é que as mensagens
consumidas enquanto o Processor Service
estiver INDISPONÍVEL sejam PERDIDAS
AutoAck
HABILITADO
INDISPONÍVEL
33. Exemplos - AutoAck Desabilitado – Cenário de Falha
31
• Com o K6, Iremos simular 10 usuários
virtuais postando uma mensagem a cada
100ms, durante 30s
• Pode gerar o máximo de 3000
mensagens, porém, devido aos tempos
de resposta da API, será menos que isso
• Manteremos o Consumer Service com
AutoAck DESABILITADO, ou seja,
enviaremos o ACK manualmente
• O Processor Service ficará INDISPONÍVEL
por alguns instantes
• O resultado esperado é que 100% das
mensagens enviadas sejam processadas
no Processor Service após o
restabelecimento do serviço
AutoAck
DESABILITADO
INDISPONÍVEL
* Garantia de ordem ocorre somente se as mensagens forem publicadas em 1 channel, 1 exchange,
1 queue.
* Em versões anteriores a 2.7.0. a ordem ainda pode ser perdida por ações de requeue
34. Exemplos – TTL – Time-to-live
32
• Com o K6, Iremos simular 10 usuários
virtuais postando uma mensagem a cada
100ms, durante 30s
• Pode gerar o máximo de 3000
mensagens, porém, devido aos tempos
de resposta da API, será menos que isso
• Manteremos o Consumer Service com
AutoAck DESABILITADO
• Manteremos o Processor Service
DISPONÍVEL durante todo tempo
• Tornaremos o Consumer Service
INDISPONÍVEL por 10s
• Configuraremos o TTL de 5s nas mensagens
• O resultado esperado é que algumas das
mensagens enviadas durante a
indisponibilidade do Consumer Service
sejam PERDIDAS
AutoAck
DESABILITADO
INDISPONÍVEL
Por 10s
TTL=5s