Esta página faz parte dos meus desenvolvimentos para a disciplina MAC0215.

Estudando o Protocolo AMQP

Esta página contém a consolidação do que foram por volta de 35 horas de estudo do protocolo AMQP versão 0.9.1.
O estudo se fez por meio da leitura da documentação (disponível no site oficial do protocolo, estudo de fontes como documentação do RabbitMQ, e experimentação usando o broker RabbitMQ e o pacote amqp-tools, disponível no github.

A documentação tardia desse estudo se deve a um semestre muito atribulado, incluindo a escrita da proposta de projeto para pleitear uma bolsa PIBIC.

por ~igfd em

Como o protocolo funciona

O protocolo AMQP divide seus participantes entre "clientes", que podem ser tanto "remetentes" quanto "assinantes", a depender da sua interação com as mensagens, e o "broker", entidade centralizada responsável por fazer a distribuição das mensagens e gerenciamento das estruturas.
Uma das características de destaque do AMQP frente a outros protocolos pub/sub é o uso de exchanges, que determinam a maneira como as mensagens serão roteadas, e filas, que são ligadas às exchanges e armazenam as mensagens a serem consumidas em regime First-In-First-Out. Um assinante seleciona uma exchange e cria nela uma fila ou consome uma fila já existente.
Para publicar uma mensagem, o remetente seleciona uma exchange e uma chave de roteamento, que serve o propósito do tópico. As variações de comportamento por exchange incluem rotear para filas que têm chave igual à chave de roteamento, para filas que correspondem parcialmente à chave de roteamento, para todas as filas na exchange, e métodos de roteamento mais complexos baseados nos cabeçalhos da mensagem.

Estruturação do protocolo

Lendo a especificação, temos que o protocolo estabelece cinco tipos de dado:

  1. Inteiros, podendo ser de 1 a 8 bytes, sempre sem sinal;
  2. Bits, ocupando 1 byte;
  3. Strings curtas, podendo ter até 255 bytes;
  4. Strings longas, de tamanho indefinido (ainda)
  5. Tabelas de campos, que carregam pares chave-valor com tipagem explícita.

Além disso, experimentando com o Wireshark e o amqp-tools, foram obtidas (e depois confirmadas pela leitura da especificação) as seguintes propriedades:

Estrutura de Pacotes

Os pacotes do protocolo são todos estruturados em uma maneira padronizada, o que facilita a implementação de máquinas de estado para verificação e classificação destes.
A estrutura geral dos pacotes é a seguinte:

Os tipos de pacote são divididos entre Método, Header, Body, os mais comuns, e alguns outros mais específicos, como Heartbeat.

A maior parte do overhead do protocolo se dá por trocas de pacotes do tipo Método, sendo estes divididos em várias classes, sendo as principais:

Os argumentos do método têm tipos específicos a depender do método. Por exemplo, o método Connection.Start, primeira mensagem do broker para um cliente, têm os seguntes argumentos:

Esses argumentos e seus tipos estão também descritos na especificação, facilitando a implementação contida da interpretação desses pacotes, uma vez identificados.

Explicação dos Métodos

Cada método será descrito da seguinte forma:

Classe Connection

Todas essas mensagens são enviadas pelo canal 0, reservado para essas comunicações.

Classe Channel

Todas as mensagens são enviadas pelo canal de interesse.

Classe Queue

Classe Basic

Um pouco sobre exchanges

Como foi mencionado no começo, o protocolo usa de Exchanges para determinar diferentes mecanismos de roteamento das mensagens publicadas. Essas exchanges podem ser definidas pela implementação de cada Broker, mas o protocolo determina quatro tipos de exchange padrão, sendo elas:

Há um conjunto de métodos não explorados aqui dedicados à criação de Exchanges.

Analisando as capacidades do protocolo

A partir desse resumo do que o protocolo suporta, podemos estipular algumas capacidades e casos de uso.

A primeira e principal capacidade que salta é a possibilidade de implementar mensageiros. Um broker central e dois clientes diferentes, cada um consumindo uma fila e ciente da fila que o outro consome, podem trocar mensagens entre si. Se a comunicação acordada é em turnos, ou seja, se o cliente A publica, deve esperar uma mensagem do cliente B para publicar novamente, é possível reduzir o sistema a uma única fila, consumida por ambos os clientes.

Além disso, a existência do método "Get" nos indica também um comportamento de cliente-servidor clássico, no qual os clientes apenas extraem a informação desejada sem necessariamente onerar o broker com a criação de uma entidade dedicada para consumir aquela fila. Esse comportamento pode se mostrar útil em um sistema onde há uma quantidade limitada de recursos que podem ser obtidos: no caso de uma distribuição de tickets para um evento, por exemplo.

Voltando agora nas capacidades de troca de mensagens, temos que, com as diferentes exchanges, é possível implementar um sistema de chat de um jogo online, com as seguintes capacidades:

  1. Mensagens privadas, utilizando uma fila para cada usuário, na exchange "amq.direct";
    • Vale mencionar aqui uma característica do header de uma mensagem que não havia sido citado: a propriedade Reply-To, que indica o endereço (uma fila, no caso) no qual aquela mensagem deve ser respondida.
    • Assim, é possível implementar não só o comando usual /whisper <usuario> <mensagem> para enviar uma mensagem privada como também o comando /reply <mensagem> para responder a última mensagem recebida.
  2. Mensagens de guilda, utilizando a exchange "amq.topic" com uma fila para cada usuário usando a chave "nome-da-guilda.#" e as mensagens sendo publicadas com a chave "nome-da-guilda.mensagem-membro.nome-do-remetente", por exemplo; e
  3. Mensagens globais, como anúncios de sistema ou mensagens de moderação, utilizando filas na exchange "amq.fanout".
    Pode-se também imaginar um uso para a exchange "amq.match" para que mensagens do servidor de jogo como, por exemplo, venda de um item no mercado do jogo possam ser convertidas em uma mensagem com o header "Vendedor" que conteria o nome do jogador que vendeu o item, e esse jogador receberia a mensagem roteada pelo campo do header.

No mais, temos a capacidade de utilizar esse sistema no contexto de IoT, utilizando sensores, atuadores, e uma entidade "decisora" (que pode estar na mesma máquina que o broker, já que esta já deve ser mais potente).
Assim, sensores de umidade podem todos enviar seus dados em uma fila "umidade" da exchange padrão, sensores de temperatura podem enviar seus dados em uma fila separada "temperatura" da mesma exchange, e a entidade decisora consome a mensagem das duas filas.
Essa entidade pode, então, fazer os cálculos necessários e, se decidir que o conforto térmico de um cômodo ou da casa toda está baixo, pode enviar uma mensagem ou aos atuadores específicos ou enviar uma mensagem com a chave "atuadores.temperatura" na exchange "amq.topic" para ativar todos eles.