O que é o RabbitMQ, e como utilizar?

Ramon Paolo Maram
13 min readMay 31, 2022

--

Logo RabbitMQ

Nesse post, iremos falar sobre o que é o RabbitMQ, bem como boas práticas, casos de uso, e provedores que nos ajudam a utilizar o RabbitMQ sem gerenciar servidor.

O que iremos precisar para esse tutorial?

Precisaremos que tenha um conhecimento de:

  • Arquitetura microservices básico
  • Docker
  • Docker-Compose
  • NodeJs
  • Express

Para que serve o RabbitMQ?

O RabbitMQ é um Message Broker. Ok, mas o que é um Message Broker?

Um Message Broker, seria um intermediador de comunicação(nesse caso, de mensagem). Ele é o software/protocolo que intermedia a comunicação de mensagens, entre sistemas.

O RabbitMQ, funciona principalmente, sobre o protocolo AMQP(Advanced Message Queue Protocol), que é amplamente utilizado na criação de sistemas com arquitetura microservices.

Grandes empresas utilizam o protocolo AMQP para comunicação de seus sistemas, por ser um protocolo robusto e seguro, no quesito de entrega de mensagens assíncronas entre serviços.

De grandes empresas que utilizam o RabbitMQ/AMQP, posso citar: Amazon, Netflix, Twitter, Microsoft, eBay, Reddit, NASA, etc…

São várias empresas que utilizam o RabbitMQ/AMQP. As empresas que utilizam, você pode ver mais clicando aqui e clicando aqui.

Percebemos que existem diversos Messages Brokers além do RabbitMQ, como o Apache Kafka, Redis Pub/Sub, AWS SQS, IBM MQ, Azure MQ entre outros. Não irei falar a diferença entre eles, pois o artigo iria ficar mais comprido ainda, mas acho interessante vocês saberem o diferencial de cada um deles.

O caso de uso do RabbitMQ(entre outros), se dá quando se deseja utilizar arquitetura microservices, quando um serviço precisa se comunicar com outro serviço, e essa comunicação não pode ser feita via API Web HTTP, pois a chamada API Web HTTP, requer obrigatoriamente uma chamada de sucesso, logo, ela é síncrono, diferente do RabbitMQ, que é assíncrono, e não obriga uma resposta/processamento imediato.

Imagina por exemplo, o sistema de publicar post no Instagram. Você seleciona a foto e publica no Instagram. Como você percebe, a foto não é publicada imediatamente, ela demora alguns segundos para ser publicada. Você não precisa ficar preso na tela do celular, principalmente na tela de publicar conteúdo, enquanto a publicação está sendo publicada.

Você apenas envia a imagem, que é enviado em uma requisição POST no serviço de recebimento de chamadas, e após isso, essa requisição de publicação é adicionado em um tópico do RabbitMQ, no qual um outro serviço irá receber a mensagem e começar a tratar ela, de forma independente. Em seu caso, você irá ficar na tela do usuário, apenas até receber a resposta com status 200, que é quando o servidor do Insta diz que recebeu seu pedido de criação de Publicação, e o resto, é cuidado internamente, de forma assíncrona.

Percebe-se que o tempo do usuário na tela, diminuiu de vários segundos, a menos de 5 segundos.

É uma melhora e tanto para o usuário, pois não precisa ficar na tela de publicação esse tempo todo, sem fazer nada. Mas também é bom para as empresas, e irei explicar do porquê, agora mesmo.

Resumindo: O RabbitMQ, é um poderoso Message Broker, que fornece meios seguros e escaláveis para uma aplicação microservices se comunicar entre os serviços.

Como as empresas se beneficiam do RabbitMQ?

Com o RabbitMQ, as empresas conseguem criar sistemas altamente escaláveis, com pontos de falhas únicos, assíncronos e robustos.

Continuando no exemplo do Instagram, vimos que os usuários se beneficiam disso, vendo que não precisam ficar preso na tela do celular, até a publicação ser realizada.

Imagina que o Instagram, tem 5 microserviços, e esses 5 microserviços são independentes entre si, pois se um falhar, não irá afetar o outro. Agora, imagina que esses 5 microserviços, possuem 2 replicas cada. Logo, temos 5 serviços, e 10 servers.

Quando você diz ao Instagram que vai publicar, o Instagram pega os dados que você enviou(imagens), e então, coloca em um tópico do RabbitMQ, onde um serviço(dois servers), serão responsáveis por fazer o “resize” da imagem, em diferentes resoluções.

Como existem dois servidores para um serviço, a mensagem enviada, irá para um desses servidores. Por padrão, o RabbitMQ funciona com o algoritmo Round-Robin, que basicamente faz um ouvinte(consumer) receber a mensagem por vez. Logo, se temos 2 servidores, e o primeiro servidor foi o último a receber uma mensagem, o servidor 2 que irá receber a próxima. E assim ficará nesse Loop de 1 -> 2 -> 1 -> 2 -> 1 -> 2….

Isso ajuda o recurso computacional ser bem gasto, ficando proporcional. Se um servidor recebeu 50 mensagens em 1 segundo, o segundo servidor recebeu provavelmente 50 mensagens em 1 segundo também.

Caso o sistema precise escalar dinamicamente, o RabbitMQ também poderá ser utilizado, pois o servidor apenas precisará se conectar com ele via URL apenas. Você pode se conectar com RabbitMQ, tanto com 1 servidor, ou até milhares deles.

Outro benefício do uso do RabbitMQ, é o ponto de falha único, que é utilizado graças a mensagem assíncrona. Como eu disse anteriormente, o sistema não precisa esperar uma resposta, ou fazer requisição a um servidor, para iniciar o processamento da mensagem. Quando um serviço/servidor estiver disponível, automaticamente ele irá ouvir as mensagens do RabbitMQ, e dar continuidade nas mensagens presentes na fila.

Imagina um caso por exemplo, onde você tem 2 servidores, e um servidor ocorreu um problema, e ficou offline. Apenas um servidor irá fazer a tarefa/processamento, mas irá realizar. Agora, caso os 2 servidores caiam, as mensagens ficarão armazenadas no sistema de filas do RabbitMQ, e poderão ser lidas(consumidas) quando o servidor voltar ao ar.

Resumindo: O RabbitMQ, permite que criemos sistemas escaláveis e independentes entre si. Mesmo se os servidores, que estão ouvindo o tópico, ficarem offline, as mensagens, ficaram salvas para serem processadas depois.

Projeto prático usando RabbitMQ com NodeJs

Irei agora, mostrar um projeto prático usando o RabbitMQ com NodeJs e Docker.

O código poderá ser encontrado aqui.

Sinta-se livre para visualizar o código todo, e caso deseje realizar alguma mudança, poderá fazer PR a vontade.

Continuando, o README.md do projeto, já diz como o executar. Basta ter o Docker instalado na sua máquina.

Como você consegue ver, temos dois serviços na nossa aplicação, sendo um o “consumer”, e o outro, o “sender”. Como deve imaginar, um serviço é o responsável por enviar a mensagem para o tópico(também conhecido como producer), e o outro serviço, é utilizado para consumir a mensagem do tópico(consumer).

Como poderá também ver no docker-compose.yaml no projeto, o serviço consumer possui 2 replicas. Esse serviço possui 2 replicas, para eu mostrar a vocês, o algoritmo Round Robin na prática.

Primeiro de tudo, você poderá executar o projeto, com o guia no README.md, rodando o comando: ‘docker-compose up — build -d’.

Após rodar o comando, você poderá acessar a URL: https://localhost/sender/Mensagem

Esse endpoint, como você pode visualizar no arquivo index.ts dentro do serviço/pasta sender/, ele pega o parâmetro “Mensagem”, transforma em string, e transforma em Buffer, para então depois, enviar em um tópico, para o outro serviço poder consumir a mensagem.

Como pode ver na imagem acima, o usuário faz uma requisição no navegador, com a URL: “https://localhost/sender/Mensagem”, e então, o Express detecta a chamada API, pega a mensagem pelo método “req.param”, e então, envia a mensagem para a fila “topic”, onde será adicionado em um sistema de filas administrado pelo RabbitMQ.

Bom, agora que vem a mágica do RabbitMQ. Após o RabbitMQ receber a mensagem, ele manda para o primeiro “servidor” conectado, ouvindo o canal. Logo, por mais que temos 1 serviço chamado Consume, e esse serviço tenha 2 servidores, apenas 1 servidor irá receber a mensagem, por padrão.

Ou seja, quando enviamos uma mensagem para um tópico, apenas uma máquina irá receber a mensagem, e não todos os ouvintes.

Você pode ver isso, acessando a URL(pela primeira vez): “https://localhost/sender/Mensagem”, e acessando a URL: “https://localhost/”, você poderá ver talvez, o campo “text” vazio ou preenchido com a “Mensagem”.

Mas, porque talvez? Como eu disse, apenas um servidor recebe a mensagem, e executa. E o nosso processo/tarefa, é receber a mensagem, e salvar em um arquivo .txt na máquina do servidor. Logo, se você acessou a URL do “sender” apenas 1 vez, apenas 1 servidor vai ter o campo “text” preenchido, e o outro não. Se você recarregar a página do “sender”, os dois serviços agora terão a mensagem.

Como eu disse anteriormente, o RabbitMQ funciona com o algoritmo Round Robin, e as mensagens são enviadas em forma de sequência, para cada servidor.

Algoritmo Round Robin

Segurança no RabbitMQ

Por padrão, o RabbitMQ é um ambiente seguro para comunicação de serviços, mas podemos fazer ele ser mais seguro, tanto em criptografia de dados, como também, não perdemos uma mensagem da fila a toa.

TTL

Acredito eu, o mais importante a se saber sobre o RabbitMQ, é o TTL, que significa: “Time to Live”, ou em tradução livre: “Tempo de Vida”. Basicamente diz, quanto tempo de vida a mensagem vai ter naquela fila.

No código do serviço “sender”, temos uma política de TTL de 60 milissegundos:

TTL em Fila/tópico e em mensagens

Como mostrado na imagem, definimos 2 vezes o TTL, mas porque?

Bom, nós podemos setar o TTL em dois casos, sendo o primeiro, setar o TTL na fila(expires), fazendo assim, com que a fila também tenha um limite de vida. Caso ele não tenha nenhum consumer o ouvindo, durante o tempo TTL determinado, ela irá ser apagada. Já a segunda opção, é setar TTL para cada mensagem, poderá fazer isso setando o “messageTtl”.

Nesse exemplo, o TTL setado, foi de apenas 60 milissegundos. Para você ver esse exemplo do TTL em funcionamento com 60 milissegundos, basta você desligar as duas máquinas do serviço “consumer”, e acessar a URL do “sender”, fazendo várias mensagens no tópico “topic”, e quando você ligar novamente o serviço “consumer”, ele não irá ouvir mensagens, pois as mensagens e o tópico, “venceram da validade”, e já não estão mais no sistema de filas, para serem ouvidas.

O tempo máximo que uma mensagem/fila, pode ter de TTL, é de atualmente 2³², que dá 4294967296ms, que convertido em dias, dá entorno de 49 dias. Logo, uma mensagem pode ser processada em até 49 dias ao entrar na fila, e um tópico pode ficar sem um consumer, por até 49 dias.

DLE

DLE(Dead Letter Exchange), ou da tradução literal: “Troca de carta morta”.

O DLE é responsável por pegar a mensagem/fila morta, e colocar em uma nova fila.

Quando setamos que o TTL das mensagens, são de 1000ms(1 segundo), e após 1 segundo, as mensagens não são ouvidas, elas serão apagadas da fila atual em que elas estão. Porém, com o DLE, nós podemos pegar essa mensagem que vai ser apagada, e no momento que ela for apagada, ela vai ser colocada automaticamente em uma outra fila. Você pode utilizar isso, para visualizar as mensagens que não foram processadas, para visualizar do porque o sistema não as processou. Mandando essas mensagens apagadas, para um outro serviço as analisar por exemplo.

Dead Letter Exchange

Durable e Persistent

A propriedade “durable”, faz com que a maioria das mensagens, sejam gravadas na memória, e também no disco do servidor.

Bom, já que falei sobre isso, é importante falar que as mensagens do RabbitMQ, em sua maioria, são armazenadas na memória RAM, para ter um rápido input/output para envio e recebimento de mensagens. Por isso é importante ter um computador, com principalmente, bastante memória RAM para usar o RabbitMQ.

Durable setado como “true” é uma boa prática, pois quando acontece algum problema com o RabbitMQ em si, as mensagens da fila não são perdidas, pois elas continuam salvas no armazenamento(HD/SSD) da máquina, e podem ser recuperadas mais facilmente do que na memória RAM, que ao computador se reiniciar, já se perdem as mensagens.

Com durable habilitado, saiba que as mensagens não irão ter uma mudança no desempenho, pois as mensagens ainda serão armazenadas na memória RAM, para entrada/saída de dados.

Ops: Apenas falando rapidamente sobre desempenho. Como o RabbitMQ grava as mensagens, por padrão na memória RAM, a memória RAM pode ficar sem espaço, e caso isso ocorra, automaticamente o RabbitMQ irá jogar as velhas mensagens da fila, para o armazenamento, fazendo assim, com que nesse caso específico, você irá notar a diferença de velocidade de recebimento de mensagens.

Para o funcionamento da fila com “durable”, a mensagem deverá ser marcada como “persistent”, pois poderão haver mensagens em que você não irá desejar que sejam armazenadas no HD/SSD, por questão que não precisam ser armazenadas com persistência no disco, economizando espaço.

Durable e Persistent

TLS

O famoso TLS(Transport Layer Security), também é suportado no RabbitMQ(AMQP). Recomendo o uso do TLS, para aumentar mais a segurança do trafego de mensagens na rede.

Por padrão, a porta do uso do RabbitMQ com TLS, é a porta 5671.

AutoDelete

A propriedade “autoDelete”, no método “assertQueue”, sendo valor “true”, permite com que quando a fila não tiver um consumer, o tópico e suas mensagens, serão inteiramente apagadas.

Eu sempre deixo o valor de autoDelete como “false”(default), pois não quero que as minhas mensagens da fila, sejam apagadas por um problema de outros serviços.

FIFO

Acredito não ser uma prática de segurança em si, mas mesmo assim, irei falar especificamente sobre o FIFO.

FIFO nada mais é, do que um acrônimo de “First In, First Out”, ou seja, “primeiro a entrar, primeiro a sair”. Basicamente, diz a ordem sobre a entrega de mensagens, para os consumers.

Imagina que por exemplo, temos 4 mensagens: “1”, “2”, “3, “4”. A primeira mensagem enviada, foi a mensagem “1”, a segunda mensagem foi a “2”, a terceira mensagem foi a “3”, e a última mensagem foi a “4”.

Pelo sistema FIFO, a primeira mensagem a ser ouvida pelo consumer, vai ser a mensagem “1”, seguida da “2”, “3” e “4”.

Em minhas aplicações, nunca precisei necessariamente processar informações em ordem, porém, acho bom o uso do padrão de fila FIFO, para não deixarmos uma mensagem muito tempo na fila, sem processar ela por exemplo.

Exemplo FIFO

Como descrito na imagem acima, a primeira mensagem a ser enviada para a fila/tópico, será a primeira a sair da fila/tópico.

RabbitMQ na Nuvem

A melhor maneira de se criar um sistema com arquitetura microservices, que utiliza RabbitMQ como Message Broker, é utilizar algum provedor cloud, para gerenciar o server do RabbitMQ.

Nesse exemplo, eu irei utilizar a CloudAMQP, pois ela é um cloud simples, tem plano gratuito e que atende as minhas necessidades, e irá atender as nossas necessidades.

Antes de dar continuidade, eu gostaria de dizer, do porque eu escolhi a CloudAMQP para meus projetos, ao invés de utilizar o AWS SQS.

O principal motivo, foi que a AWS SQS, permite 1 milhão de chamadas, apenas durante o Free Tier, e após esse período, será cobrado. Claro, é um serviço muito barato, principalmente para a quantidade que irei utilizar, mas ainda sim, é um custo. E eu também, não irei utilizar 1 milhão de chamadas por mês.

Voltando ao assunto, a CloudAMQP tem um plano gratuito, que permite:

  • 10k de mensagens em uma única fila
  • 100 Filas/Tópicos
  • Permite que um tópico fique em IDLE(sem uso), durante até 28 dias.
  • 20 conexões simultâneas
  • 1 milhão de mensagens totais por mês.(100 filas * 10k de mensagens)
  • Poderá enviar/receber até 3 mensagens por segundo.

O plano gratuito da CloudAMQP, me parece muito bom, e atende sem problema minhas necessidades.

Claro, nós temos um problema aqui, principalmente se você for utilizar em produção.

  • Não tem replication de dados
  • Baixo envio/recebimento de mensagens por segundo
  • Instância compartilhada, logo, quando um usuário na mesma instância, usa bastante a rede, o seu desempenho será comprometido.

Então caso decida lançar um produto final, usando a CloudAMQP, pegue o plano com instância dedicada.

Para mais informações sobre os planos, clique aqui

Dando continuidade no nosso projeto, agora precisamos criar uma instância. Basta você selecionar o plano que deseja, autenticar com a sua conta, e seguir os passos descritos na tela.

Após isso, você verá uma tela como essa:

CloudAMQP

Basta copiar o campo “URL” em “AMQP Details”, e colar no nosso .env, ou diretamente no código. Como irei publicar o projeto no GitHub, irei colocar a URL no .env dos meus dois serviços.

Ops: Para executar usando a CloudAMQP, nós não conseguiremos setar o “expires”, “messageTtl” e “DLE”. Então teremos que comentar essa linha.

Após utilizar a variável de ambiente para proteger a minha URL, poderei executar o projeto novamente.

docker-compose up --build -d

Após tudo ter dado certo, podemos enfim, acessar a URL do serviço sender novamente, e vermos se a mensagem foi enviada com sucesso, para o serviço consumer, acessando o endpoint “/”.

Agora que mudamos de um serviço local, para um serviço na nuvem, nós temos outras vantagens. Agora, nós podemos visualizar as métricas da nossa instância. Nós podemos ver, quantas conexões tem ativadas, quantas mensagens foram enviadas, quanto tempo demora para um consumer receber uma mensagem, e dar um ‘ack’. São métricas valiosas, para melhorarmos a nossa aplicação.

Na tela do navegador da web da CloudAMQP, clique no botão verde, escrito: “RabbitMQ Manager”, e você será levado para uma página, onde você irá ter acesso a várias informações sobre a nossa instância. Incrível não?

Finalizando

Esse artigo foi longo comparado com outros que eu vejo, porém, espero que tenham gostado : )

Nesse post, você viu:

  • O que é o RabbitMQ e o AMQP.
  • Para que utilizar RabbitMQ(AMQP)
  • Quais empresas utilizam
  • Como utilizar no NodeJs
  • Boas práticas de segurança/robustez
  • Como utilizar RabbitMQ na Nuvem

Desafios

Te proponho dois desafios:

O primeiro desafio, é para você ver o poder de ser altamente escalável do RabbitMQ com o CloudAMQP. Recomendo você aumentar as replicas no arquivo docker-compose.yaml, para 18, ao invés de 2. Assim, você irá ver que você terá 18 servidores rodando o serviço “consumer”, e eles estarão se comunicando com o RabbitMQ, de forma sequencial(Round-Robin).

Ops: Não se preocupe com o acesso aos endpoints. Por padrão, o NGINX já está cuidando do Load Balancer da aplicação.

Um segundo desafio que recomendo fazer, é criar dois serviços, um deles usando NodeJs, e um outro serviço, usando uma linguagem de sua preferência, como Python, Java ou Golang.

Você verá nesse desafio, o poder de ser tecnicamente independente de outros serviços, dependendo apenas da rede(RabbitMQ). Poderá fazer o “sender” em NodeJs, e o “consumer” um programa em Python, para escrita de dados no banco de dados sqlite por exemplo.

Conteúdo recomendado

--

--