fbpx
RabbitMQ & AMQP – #3 – Conceitos
Publicado em: quarta-feira, 20 de fev de 2019
Categorias: RabbitMQ de A a Z
Tags: AMQP | RabbitMQ

Agora é o momento de falarmos brevemente sobre cada elemento do AMQP. Nesse post ainda estamos tratando com superficialidade, mas é importantíssimo passar por aqui para progredirmos nessa jornada.

Standard vs Implementação

Standard que trata de mensageria. No AMQP não temos termos como Exchanges, Queues e Binds, vemos termos como Transport, Source e Target, portanto embora possamos olhar pela perspectiva do AMQP, tomei a liberdade de falar sobre a perspectiva de uma de suas implementações, no caso o RabbitMQ, já que é foco do nosso estudo. Mas vale muito a pena olhar o standard, os links estão logo a seguir.

AMQP é gerido pela OASIS que por sua vez cuida de dezenas de outros standards. O AMQP especificamente pode ser detalhado aqui e aqui.

O RabbitMQ é composto por poucos elementos, e exige certa criatividade para compor os diversos cenários de uso. Enfim não tem mágica nem jeitinho, você precisa de fato conhecer o fundamento. Assim vamos abordar Mensagens, entender que a função das exchanges é tão fundamental quanto entender o conceito de filas, e por isso, vou abordar exchange antes de abordar filas. Também falaremos de Virtual Host, Routing Keys e Binds, e sim, esse é o fim da explicação sobre RabbitMQ. RabbitMQ é composto somente por esses elementos.

RabbitMQ é um message broker, uma implementação AMQP, a mais usada do mercado. Com a dependência do Standard como AMQP, os problemas de impedância entre soluções de mensageria são minimizados e em alguns casos até eliminados.

Na mensageria existem papéis

Quando falamos de mensageria, devemos ficar atentos a quem exerce um papel e que papel é esse.

Ator

Vou chamar de ator qualquer parte do seu código, pode ser um serviço, um sistema inteiro, não importa muito, é alguém que realiza uma operação.

Producer

Um producer/publisher é um ator que publica uma mensagem no message broker.

Embora você possa ter o ímpeto de pensar “é quem publica uma mensagem em uma fila“, na prática vamos ver que há um intermediador, que é a exchange, e seu papel no fluxo invalida essa afirmação.

Portanto, vamos definir como alguém que está tentando publicar uma mensagem no message broker.

Consumer

Um consumer é um ator que consome mensagens de uma ou mais filas. É quem realiza o processamento de uma mensagem que está na fila.

Dissecando o RabbitMQ

Virtual Host

O virtual host é um ambiente isolado dentro de uma instância do RabbitMQ. Nele temos usuários e roles, e todos os objetos como filas, exchanges, e suas ligações, os binds. Cada virtual host é isolado dos demais. Por padrão, uma instalação do RabbitMQ vem com o Root, um virtual host como os demais, no entanto ele não possui um nome. Na uri AMQP ele é definido como amqp://user:pass@host:10000/, enquanto um virtual host AAA usaria uma uri assim amqp://user:pass@host:10000/AAA.

Na interface de gerenciamento escolhemos um virtual host por vez para gerenciar.

Mensagem

Uma mensagem é um objeto que tem o propósito de chegar a uma ou mais filas #spoiler para assim serem processada(s).

Uma mensagem AMQP consiste em um envelope, nesse envelope temos headers (semelhante aos headers http) e payload (o body efetivamente).

Os cabeçalhos são úteis para diversas finalidades, inclusive roteamento. Alguns tipos de exchange usam esses cabeçalhos em conjunto com os binds para decidir em qual ou quais filas uma mensagem deve ser enviada. Veremos isso ainda nesse post. Os cabeçalhos também são muito úteis e podem te ajudar a entender o tipo da mensagem (formato de encoding) etc.

Exchange

Exchange é um objeto programável de roteamento.

Isso quer dizer que você pode definir um conjunto de regras de roteamento. Essas regras podem fazer uma mensagem ir direto para uma fila, ou mesmo ser distribuída em diversas filas ou em outros casos ser descartada mesmo.

Há implementações de plugins para RabbitMQ em que você pode até escrever scripts de roteamento. Dê uma olhada no projeto script-exchange para entender.

Como funciona?

Um publisher, SEMPRE envia sempre uma mensagem para uma Exchange.

No RabbitMQ, não existe envio de mensagem direto para filas.

As mensagens SEMPRE passam por uma exchange.

#spoiler

A exchange pode ser de diversos tipos, e é na exchange que acontece a escolha da fila a ser enviada a mensagem (ou das filas…).

O RabbitMQ possui tipos de exchange, cada uma com um comportamento diferente.

A que você possivelmente mais vai estar habituado é a direct. A direct exerce o papel de Exchange Fake, pois ela não toma decisão alguma, ela não possui nome, faz um bypass para enviar a mensagem para a fila que possui o nome que você enviou. Em vez de enviar a mensagem para o nome da exchange, você usa o nome da fila, a exchange direct (que é padrão, mas pode ser excluída) fará você ter a impressão de que está enviando a mensagem para uma fila direto… mas lembre-se. Fisicamente isso não existe e a exchange direct pode ser removida por você.

Os tipos de Exchange são detalhados no link AMQP Concepts.

Exchanges predefinidas no RabbitMQ

NameDefault pre-declared names
Direct exchange(Empty string) and amq.direct
Fanout exchangeamq.fanout
Topic exchangeamq.topic
Headers exchangeamq.match (and amq.headers in RabbitMQ)

Filas

Pronto, enfim chegamos às filas, um dos elementos mais simples da estrutura do RabbitMQ. 

Filas são objetos internos do RabbitMQ que armazenam mensagens e gerenciam sua distribuição para os consumers. É delas que você consome mensagens. É o ponto central de qualquer arquitetura de mensageria. 

A forma de trabalho mais comum, depende de você entrar na administração do RabbitMQ, e criar filas com nomes específicos, configurações etc. Essas filas, geralmente criadas assim “possuem CPF”, isso quer dizer, seu nome representa algo relevante para você e para sua arquitetura. Elas são referenciadas, você armazena esses nomes em strings de configuração para poder enviar como parâmetro de roteamento.

Mas outro tipo de fila, MUITO MAIS COMUM DO QUE VOCÊ POSSA PENSAR, são as filas dinâmicas, criadas por um consumidor. 

Talvez, nesse momento você tenha se perguntado: Mas como? Como eu vou criar uma fila dinamicamente, onde cairiam as mensagens enquanto eu não criar a fila?

Antes de ser mais formal, entregando uma definição a respeito dos tipos de filas vou dar um exemplo:

Imagine um cluster com 10 máquinas, consequentemente 10 serviços, cada 1 em uma máquina. Imagine que eu preciso notificar cada uma das máquinas para que todas façam a mesma atividade. Lembre-se, como um cluster tolerante a falhas, as máquinas podem cair. No entanto vamos supor que minha mensagem só faça sentido para quando as máquinas estivessem online. Isso quer dizer que um nó fora do ar, não deve receber mensagem alguma. Como resolver isso?

A gente deve criar, manualmente uma exchange com um nome que faça sentido.

No ator que consumirá a mensagem da fila, devemos adicionar (em 1 ou 2 linhas de código) as chamadas para a criação de sua fila. Essa fila será:

Anônima: Não possui um nome predefinido, o RabbitMQ gera um nome dinamicamente e devolve no momento da criação.

Exclusiva: somente 1 consumidor poderá consumir mensagens dessa fila.

Auto-delete: Caso seu consumidor caia ou morra ou o cluster saia do ar, a fila precisa ser excluída, pois não faz sentido sua existência mais.

Por fim, na segunda linha criamos a associação entre a exchange e essa fila que acabamos de criar. O único cuidado, e você deve estar se perguntando sobre isso, é para usarmo so o tipo adequado de exchange para que possamos garantir que uma mensagem chegará a todas as filas.

Assim, temos 2 tipos de filas:

  • Nomeadas – Filas que possuem um nome pré-definido. Isso quer dizer que você previamente a cria, seja via API ou via Management Console. São filas que possuem “CPF”, o nome dela tem relevância para sua implantação. Essas em geral, são filas que nunca morrem, elas nascem e duram a vida toda.
  • Anônimas – São filas criadas dinamicamente por demanda. Esse tipo de fila é alicerce das implementações de RPC, por exemplo, é na fila anônima que recebemos o response. Isso acontece pois ao criar uma fila anônima, seu provider AMQP recebe no código o nome da fila, criado dinamicamente pelo RabbitMQ. Esse nome é útil para criar associações entre exchange e fila, por exemplo, ou para enviar no cabeçalho de uma mensagem, quando você está esperando uma resposta de uma requisição nessa fila.
  • Durável ou não durável: A fila persiste entre restarts do RabbitMQ?
  • Exclusive / Não exclusiva: Somente 1 consumidor poderá consumir a fila, e quando o consumidor cair a fila será deletada.

Com essas configurações, e um pouco de engenhosidade fazemos muitas coisas, usamos as abordagens mais diversas possíveis. Mas é preciso atenção. Muito cuidado com os detalhes, ter certeza de que está usando os tipos de recursos adequados.

using System;
using RabbitMQ.Client;
using System.Text;

class Send
{
    public static void Main()
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using(var connection = factory.CreateConnection())
        using(var channel = connection.CreateModel())
        {
            channel.QueueDeclare(queue: "hello",
                                 durable: false,
                                 exclusive: false,
                                 autoDelete: false,
                                 arguments: null);

            string message = "Hello World!";
            var body = Encoding.UTF8.GetBytes(message);

            channel.BasicPublish(exchange: "",
                                 routingKey: "hello",
                                 basicProperties: null,
                                 body: body);
            Console.WriteLine(" [x] Sent {0}", message);
        }

        Console.WriteLine(" Press [enter] to exit.");
        Console.ReadLine();
    }
}

Tirado do próprio site do RabbitMQ, na linha 13 vemos a criação de uma fila:

  • Nomeada
  • Não durável
  • Não exclusiva
  • Sem Auto-delete

Na linha 22 vemos a publicação de uma mensagem

Pela ausência do nome da exchange, sabemos que esse ator espera que a exchange (direct) seja usada para enviar a mensagem para a fila que possui o mesmo nome da routing key

Bindings

Por fim o último conceito necessário para entender AMQP são os bindings, que consistem em associações entre exchange e filas ou entre exchanges.

O bind possui uma routing key, essa routing key é uma chave de roteamento usada de formas diferentes e pode ser até ignorada. Tudo depende do tipo de exchange.

Exchanges do Tipo:

TOPIC: usam a routing key como um pattern. O match é feito contrastando a routing key da configuração com a routing key da mensagem.

FANOUT: ignoram a routing key, considerando apenas a existência do bind. A existência da routing key no bind não produz erro, mas nesse caso é ignorado durante o roteamento.

DIRECT: usam a routing key como uma chave, que precisa fazer match exato. A routing key da mensagem deve ser idêntica à routing key do bind.

Ainda existem comportamentos especiais como da exchange “amqp.direct” que, mesmo não tendo bind, realiza o roteamento direto, usando a routing key da mensagem como o nome da fila de destino. Essa exchange roteia as mensagens buscando a fila com o mesmo nome da routing key que veio da mensagem.

Próximos passos

Com esse post, acabaram as definições a respeito do RabbitMQ, amanhã teremos um QnA (Perguntas e Respostas), com as dúvidas mais comuns de quem está vendo filas pela primeira vez.

Você pediu e agora virou curso. Mensageria .NET é minha formação de especialista em RabbitMQ com .NET, onde ensino RabbitMQ do básico, cada fundamento, cada detalhe, ao avançado.

Onde você vai sair do zero absoluto e vai conseguir criar, projetar e corrigir soluções .NET com RabbitMQ.

Além de contar mais 3 outros bonus incríveis para ajudar todos que precisam de um up na carreira.

RabbitMQ Newsletter

Novidades e ofertas de conteúdo exclusivo e único no Brasil.

Hoje com orgulho somos referência quando se fala em RabbitMQ com .NET.

São quase 10 anos usando RabbitMQ em projetos .NET com C#, implantando, convencendo times e mostrando o caminho para aslcançar sos 5 benefícios.

Após centenas de pedidos, criei um curso dedicado aos profissionais .NET. 

Aqui nessa newsletter eu te entrego promoções e links especiais! Cola aqui, tem muita coisa legal!

Luiz Carlos Faria

Meu primeiro contato com RabbitMQ foi em 2013.

Eu estava sozinho na definição de uma arquitetura para a reestruturação de uma integração enquanto meu time estava ocupado com o dia-a-dia.

Naquela época eu precisava de apenas 1 ou 2 recursos que o RabbitMQ entregava.

Nas primeiras semanas e meses em produção pude perceber coisas que não estavam escritas em lugar algum, benefícios e formas de uso das quais poderiam resolver anos de frustração.

Desde então RabbitMQ tem sido meu aliado na restruturação de projetos dos mais variados.

E por mais simples que seja, ainda é possível, 10 anos depois, gerar surpresas com novas abordagens que geram novos benefícios.

7 dias

É tudo que precisa para sair do zero, à produção!

Com conforto, com segurança, e com apoio.

Desde que você já seja um desenvolvedor profissional.

Se você quer entregar mais Disponibilidade, Eficiência, Resiliência, Confiabilidade e/ou Escalabilidade, em projetos .NET, aqui é o seu lugar.

9 Comentários

  1. Mario

    Digamos que eu tenha 3 aplicações distintas, cada uma delas utilizando suas próprias filas, exchanges, usuários etc.

    Criar um virtual host para cada aplicação seria uma boa solução para garantir que as mesmas tenham seus ambientes isolados dentro de uma instância do rabbitMQ?

    Obrigado! Parabens pelo post!

    Responder
    • Luiz Carlos Faria

      Opa Mario, sim e não, depende.
      Quanto mais estático for, e quanto menor for a quantidade de objetos por aplicação: Menor pode ser a preocupação com a separação de Virtual Hosts.

      Agora se você cria filas dinamicamente o tempo todo ou se você tem uma quantidade substancial de filas e exchanges, aí faz é quase que obrigatório.

      Um cenário em que sequer é passível de discussão é sobre ambientes (dev, test, prod etc), sobre essa ótica nunca junte dev com prod no mesmo virtual host.

      Então para resumir, os 2 cenários estão certos. Não há uma regra clara a ser seguida, o que eu expliquei, é o que eu aplico no meu dia-a-dia.

      Responder
      • Mario

        Muito obrigado pela resposta, muito esclarecedora! Muito obrigado mesmo!

        E parabens pelo seu trabalho, acompanho seus artigos e canal, tenho aprendido muito!

        Grande abraço.

        Responder
  2. Felipe Pragana

    Gago, Ainda sobre VirtualHosts, no caso de aplicações multi tenants, acredito que faria sentido isolar cada cliente, correto?

    Responder
    • Luiz Carlos Faria

      O fato de ser multi tenant não é o motivo para isolar em vários VirtualHosts, depende da sua estratégia de utilização do RabbitMQ.

      Responder
    • Luiz Carlos Faria

      Respondi melhor com audio no grupo de alunos.

      Responder
  3. juliano cesar

    excelente, é a primeira vez que estou mexendo com rabitmq, sou profissional devops, e já atuei com confluentcloud, no caso da confluent, tinhamos 3 ambientes, laboratorio-testes, hml e prd. e para cada ambiente tinhamos um cluster criado, aí queria saber, no caso do rabitmq, eu só tenho que instalar ele em um servidor apenas(centos por exemplo), e, quando acessar a interface(sou deficiente visual e, o confluentcloud tinha alguns problemas de acessibilidade na interface web, mas eu me virava tranquilo).eheheh. continuando, e na interface eu criar os virtual hosts para os ambientes das aplicações? por enquanto serão duas, mas iremos trabalhar com outras em breve.

    Responder
    • Luiz Carlos Faria

      Os virtual hosts são encarados normalmente como ambientes completamente isolados.
      Isso tem de ficar claro.
      Se uma aplicação precisa falar com outra via RabbitMQ, você cria um único virtual host para todas as participantes da conversa.
      Então normalmente você tem um virtual host só para várias aplicações (todas ou quase todas). E algumas aplicações isoladas possuem seus próprios virtual hosts.
      Cada conexão com o RabbitMQ acontece por via de um único virtual host.
      Uma conexão não se conectar em dois virtual hosts.
      Então se uma aplicação consome em um virtual host e precisa enviar mensagens para outro virtual host, são necessárias duas conexões.
      Isso não é um problema quando o volume de conexões é pequeno, mas é necessário ter isso em mente para um planejamento adequado.

      Responder

Enviar um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Lives

Fique de olho nas lives

Fique de olho nas lives no meu canal do Youtube, no Canal .NET e nos Grupos do Facebook e Instagram.

Aceleradores

Existem diversas formas de viabilizar o suporte ao teu projeto. Seja com os treinamentos, consultoria, mentorias em grupo.