Spring Boot + RabbitMQ: Porque considerar o uso de mensageria no seu projeto

Visão geral sobre RabbitMQ e um projeto de exemplo

Diego Alexandro de Oliveira
TOTVS Developers
7 min readMar 24, 2020

--

Monólito

Em aplicações para empresas, é muito comum a necessidade de troca de dados entre aplicações. Se estivermos falando de aplicações de uma mesma empresa de software, este software pode estar em uma arquitetura monolítica, logo, esta troca de dados é muito fácil, já que se pode acessar a tabela que contém os dados solicitados. Ou, em outro caso, ambos os softwares podem compartilhar até o mesmo código fonte(tendo como base um framework) e um chamar a mesma função que recupera um dado.

Entretanto, hoje em dia, esta realidade está mudando: as aplicações estão começando a ficar ainda mais independentes (desacopladas), cada qual com seu banco de dados e o acesso a estes dados estão restritos.

Novos desafios

A maioria das soluções que vemos hoje em dia é a troca de informações entre softwares via WebServices (API Rest ou SOAP). Geralmente, funciona muito bem quando a aplicação ‘A’ precisa de alguma informação da aplicação ‘B’, sendo realizada uma chamada no endpoint da aplicação ‘B’ e esta informação é retornada e utilizada pela aplicação ‘A’.

Mas este cenário começa a ficar complexo se tal informação possuir algum tipo de estado e este estado muda na aplicação origem. Assim sendo, como a aplicação que já consumiu o dado será avisada? Uma das formas é via agendamento, a cada X segundos a aplicação ‘A’ pergunta se houve alteração de estado na aplicação ‘B’. É uma solução que funciona, mas não é muito elegante, pois se cria o consumo de recursos desnecessários em ambas as aplicações.

Uma forma interessante é se invertemos a responsabilidade, se o dado é originado na aplicação ‘B’, logo é responsabilidade dela avisar os interessados. Assim, toda vez que o dado mudar seu estado, a aplicação ‘B’ avisa a aplicação ‘A’ sobre a mudança. Dessa forma, não se tem desperdício de recursos e cada requisição faz sentido.

E se existir mais de uma aplicação interessada no mesmo dado? Estamos vendo cada vez mais aplicações desacopladas e que precisam de dados de outras aplicações.

A solução de ‘avisar’ os interessados ficaria muito complexa. Caso a aplicação ‘B’ precise avisar 5 outras aplicações, veja as complexidades:

  • Seria necessário conhecer todas as formas de entrada de todas as 5 aplicações.
  • Se uma das 5 aplicações não estiver disponível, qual ação deve ser tomada? Tentar novamente daqui 5 segundos? Guardar este dado e mandar novamente na próxima mudança de estado?

Seria preciso construir algo a parte só para controlar esse tipo de situação, trazendo ainda maior complexidade ao projeto.

RabbitMQ

E se utilizarmos um software de mensageria? Estes softwares são conhecidos como broker, um intermediário que realiza a gestão de troca de dados utilizando algum protocolo. No caso do RabbitMQ, o protocolo mais utilizado é o AMQP 0.9.1.

Por meio do RabbitMQ, estaremos delegando a responsabilidade de troca de informações:

  • A aplicação que cria o dado não precisa conhecer quem precisa do dado
  • Em caso de indisponibilidade das aplicações, o RabbitMQ guarda a mensagem até a aplicação voltar
  • Não há requisições desnecessárias entre as aplicações
  • Tudo ocorre de forma assíncrona

Como o RabbitMQ funciona?

A aplicação que cria a mensagem é chamada de producer. Esse producer envia a mensagem para uma exchange.

A exchange é como uma porta de entrada, ela é responsável de receber as mensagens e decidir quais queues irão entregar as mensagens.

A queue é uma fila de qualquer tipo de mensagem e a aplicação que deseja receber as mensagens precisar se conectar a queue, essa aplicação é uma consumer.

A queue e a exchange possuem um binding, uma ligação, isto significa que a queue está pronta para receber as mensagens da exchange.

Veja um fluxo básico de uma mensagem no RabbitMQ:

  • Producer envia mensagem para exchange
  • Exchange avalia e entrega a mensagem para a queue ligada a ela
  • Queue entrega a mensagem para o consumer

As exchanges possuem tipos e cada tipo pode mudar completamente o fluxo da mensagem.

Direct

Uma exchange do tipo direct irá entregar a mensagem para as queues ligadas a ela.

Exchange Direct

Neste tipo é possível usar a routing key, cada mensagem pode possuir uma routing key, e o binding entre a exchange e a queue também pode possuir uma routing key, logo as mensagens que possuem exatamente a mesma routing key utilizada no binding irão receber a mensagem.

Fanout

Todas as queues ligadas a exchange receberão as mensagens. Esse tipo de exchange não suporta routing key, então ele é mais utilizado como um broadcast, todos interessados irão receber as mensagens sem nenhum tipo de filtro.

Exchange Fanout

Topic

É uma mistura entre direct e fanout, onde uma exchange pode ter varias queues ligadas, e cada mensagem recebida pela exchange será replicada para as queues.

A diferença é que no topic é possível usar a routing key usando alguns patterns.

Um binding que possui a routing key “#.image” irá receber mensagens com a routing key “mountain.image” ou “house.image”, por exemplo. Mas não receberá mensagens com routing key “house.info” ou “house.floor.image”, ou seja, o # significa qualquer texto naquele nível.

Já o * irá aceitar qualquer texto em qualquer nível da routing key. Se o binding usar a routing key “*.image”, será aceita qualquer mensagem com routing key terminada em “.image”, por exemplo “house.floor.image”.

Exchange Topic

Headers

O tipo headers é muito parecido com o topic, a diferença é que não suporta a routing key, mas o filtro de mensagens é feito no header da mensagem.

Os headers são uma estrutura de chave-valor e é possível configurar no binding se a mensagem deve possuir todos os headers ou apenas um.

Como utilizar o RabbitMQ

A seguir será demonstrado como utilizar a aplicação do RabbitMQ, estarei utilizando o Docker para rodar um container com a imagem do RabbitMQ.

No Docker Hub existem as imagens oficiais do RabbitMQ. Em nosso exemplo, faremos uso da imagem 3.8.3-management. Estaremos utilizando a management porque essa imagem possui uma aplicação de gerenciamento do RabbitMQ (as imagens sem a tag management não possuem essa aplicação).

Para criar um container com a imagem, basta utilizar o comando abaixo:

Após alguns segundos, pode-se acessar o RabbitMQ pelo navegador por meio do endereço http://localhost:15672 .

O usuário e senha padrão é “guest”.

Se explorarmos um pouco, é possível visualizar as exchanges e as queues. Nesta aplicação, podemos criar exchanges, queues e os bindings.

Faça um teste, crie uma exchange, uma queue e execute um binding entre elas. Após isso publique uma mensagem na exchange e visualize a mensagem na queue.

Spring Boot + RabbitMQ

Agora será demonstrada a implementação de projetos Spring Boot conectados ao RabbitMQ, utilizando Angular e PO UI no frontend.

A ideia destas aplicações é demonstrar o funcionamento do RabbitMQ.

O código fonte pode ser encontrado no github.

Os projetos estão separado da seguinte forma:

  • producer-api (aplicação Spring Boot que publica mensagens no RabbitMQ)
  • producer-ui (aplicação Angular que enviar dados para o producer-api)
  • consumer-api (aplicação Spring Boot que consome mensagens do RabbitMQ)
  • consumer-ui (aplicação Angular que está conectada via WebSocket na consumer-api e demonstra as mensagens recebidas)

Producer-API

Primeiro precisamos criar a exchange, no Spring Boot para criar uma exchange é preciso criar um bean do tipo Exchange.

Nesta aplicação, toda chamada POST de criação de “Machine” enviará uma mensagem para o RabbitMQ.

Após subir a aplicação (API + UI) e realizar uma inserção, pode-se verificar que foi criada a exchange.

exchange criada pelo Spring Boot

Consumer-API

A aplicação que receberá a mensagem precisa configurar o RabbitMQ. O código abaixo está informando ao Spring Boot a necessidade de criar uma exchange, uma queue e o binding entre os dois.

Já o consumo da mensagem é feito utilizando a anotação @RabbitListener em um método que recebe como parâmetro um tipo Message. Desta forma o Spring Boot sabe de qual fila este método está esperando mensagens.

Após subir a aplicação é possível visualizar que foi criada a queue e o binding.

queue criada pelo Spring Boot
binding criado pelo Spring Boot

Com apenas algumas configurações, a aplicação já está habilitada para trocar mensagens por meio do RabbitMQ, muito simples e rápida.

A imagem abaixo ilustra o funcionamento entre as aplicações.

Ambas aplicações rodando e trocando mensagens

Este foi um exemplo de aplicações utilizando o RabbitMQ, espero que tenham gostado.

Lembrando que todo o código fonte encontra-se no github.

--

--