Rabbitmq
Muito melhor que uma tabela no banco
Eu venho de uma época em que usávamos banco de dados para tudo , a forma padrão de integrar aplicações era através de tabelas e controle de status.
Sei que você torceu o nariz , mas é assim que é em muitos lugares , conforme essas integrações foram crescendo e conforme a infraestrutura vai se tornando mais complicada ,qualquer desenvolvedor que mantem um sistema por mais de um ano, acaba percebendo que isso tende a se tornar uma pedra no sapato da aplicação, sempre que a aplicação fica lenta , ouvimos aquele frase celebre , “Será que tem algum job rodando?” , mas será que não da pra fazer melhor?
- Imagine matar todos aqueles jobs que batem no banco da sua aplicação e deixam o banco “sentado”?
- Imagine tirar do pipe da aplicação aqueles processo pesados e deixa-los isolados em outros sistemas focados em realizar apenas essa tarefa ?
- Imagine conseguir integrar sistemas em um fluxo , onde um processo maior pode ser executado como partes isoladas em aplicações diferentes, servidores diferentes, sem usar banco de dados para gerenciar isso?
Isso é exatamente o que uma grande parte do mercado faz hoje em dia ,então podemos começar também. Esse cara pode liberar nossos bancos de leituras massivas, mas antes disso vamos entender o básico do RBMQ.
De uma olha no modelo;
- Produtor : Quem gera as mensagens pode ser sua api
- Exchange: Um tipo de Proxy de mensagem, uma peça do protocolo que o Rabbit implementa o AMQP, ele pode ter alguns comportamentos específicos como Direct, Fanout, Topic por enquanto vamos entender que são apenas regrinhas de como as mensagens vão ser encaminhadas, esse processo é chamado de binding e faz parte da arquitetura do Rabbit e seus protocolos.
- Broker : É o servidor.
- Mensagens : São nossas classes d Dominio , ViewModel , Dto etc, poder ser até dados primitivos como strings, inteitos etc , no final tudo vira bytes
- Consumidor : Aplicações que lê as mensagens.
Basicamente precisamos ter alguém que produz a mensagem um Produtor , e alguem para consumir Comumidor, isso é feito por uma abstração chamada IConnection e factory ConnectionFactory que fica dentro do pacote RabbitMQ.Client eu usei uma API .net core para construir o produtor , um Serviço hospedado para ser o consumidor A e um Console para ser o Consumidor B.
Vamos instalar o pacotinho na API e no Console;
Install-Package RabbitMQ.Client
Criar a Controller Enqueue;
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "Messages",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);var messageJson = System.Text.Json.JsonSerializer.Serialize(model);
var body = Encoding.UTF8.GetBytes(messageJson);channel.BasicPublish(exchange: "",
routingKey: "Messages",
basicProperties: null,
body: body);
}
Esse cara manda uma mensagem para um servidor local, essa mensagem é uma classe uma model que estamos carecas de usar.
Serializamos utilizando o System.Text.Json, que muito mais rápido que o e NewtonSoft, transformamos em bytes, coisa fina, esses bytes são pulicados em uma exchange basicamente um roteador de mensagem.
Uma vez Publicado a mensagem vai para o servidor e podemos vela pela ferramenta de administração do Rabbit.
Para instalar o rabbit, Subi uma imagem via Docker coisa linda e simples, se vc ainda não conhece docker veja esse artigo (docker tem um linux no meu windows)
Para você por o Rabbit para rodar use o comando abaixo , ele é mais complexo por que define o nome do container , define as portas que o servidor vai responder, o parâmetro -d e para “desataxar” do container do terminal e manter ele rodando.
docker run -d --hostname rabbitserver --name rabbitmq-server -p 15672:15672 -p 5672:5672 rabbitmq:3-management
Vou usar o postmam para poder enviar a mensagem, para o endereço da minha api que no caso esta em https://localhost:44386 a nossa controller é a enqueue
e olha que coisa mais linda;
vou clicar enlouquecidamente para ver
Legal D+
Agora eu vou criar o consumidor , normalmente fazemos isso em um console para que o aplicativo fique esperando a mensagem chegar , dizemos nas trincheiras que ele fica escutando a fila.
Eu não queria fazer apenas um console como consumidor, estou pensando em usar Serviços Hospedados do .net core, é super simples , basta criar uma classe na sua própria Api que implemente a interface IHostedService, essa classe temo dois métodos simples para poder StartAsync e StopAsync , usei um timer para chamar a rotina de tempos em tempos e configurei a classe program para subir essa classe como um serviço hospedado junto com a API;
A classe DequeueHandler fica assim;
O método Consumer tem uma implementação default que peguei da própria documentação do Rabbit;
var factory = new ConnectionFactory() { HostName = "localhost" };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "Messages",
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);var consumer = new EventingBasicConsumer(channel);
var item = default(Message);
consumer.Received += (model, ea) =>
{
try
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
item = System.Text.Json.JsonSerializer.Deserialize<Message>(message);
Console.WriteLine($"Message : {item.Name}, {item.Value}");
channel.BasicAck(ea.DeliveryTag, false);}
catch (Exception ex)
{
channel.BasicNack(ea.DeliveryTag, false, true);}
};
channel.BasicConsume(queue: "Messages",
autoAck: false,
consumer: consumer);}
Para não dizer que esta exatamente igual , ao código da documentação eu mudei o autoAck, que é uma forma automática de dizer que recebemos a mensagem, não é uma ideia muito boa.
Basicamente fiz um try Cath e se não der erro eu digo que recebi assim ;
channel.BasicAck(ea.DeliveryTag, false);
Se der errado eu digo que não recebi , assim;
channel.BasicNack(ea.DeliveryTag, false, true);
RabbitMQ Round Robin Pattern
Esse mecanismo é show é já nativo do Rabbit e é ai que começamos a ter grandes vantagens pois ele consegue distribuir as mensagens para vários consumidores garantindo que um consumidor não receba a mesma msg de outro
Agora eu vou mostrar o funcionamento em um “videozinho”;
o projeto esta no git