Exchanges tipo headers no RabbitMQ
Sejá bem vindo ao penúltimo artigo desta série sobre o RabbitMQ. Muito obrigado pela sua presença e por ter chegado até aqui nessa jornada comigo sobre o mundo da mensageria e se você é novo aqui eu recomendo que você visite este link Artigos sobre mensageria. para ter acesso desde de o primeiro artigo desta série e poder tirar o melhor proveito dos conteúdo abordados aqui.
Hoje vou falar sobre o último tipo de Exchange oferecido pelo RabbitMQ, estou falando do Exchange headers. Sem dúvida o tipo mais complexo que podemos usar e também o que mais oferece flexibilidade para trabalhar nas nossas rotas. E como não podia deixar de ser vamos usar o PHP para trabalharmos com esse tipo de Exchange.
Exchange headers
Em uma Exchange do tipo headers diferente os tipo direct e topic não usamos a Routing keys para definirmos o roteamento das nossas mensagens. Nesse tipo como o nome sugere nós usamos o header(cabeçalhos) das mensagem para definirmos as regras do roteamento. Basicamente o header enviado nas mensagem precisa combinar com o bind(ligação) feita entre a fila e a Exchange. Vamos ao exemplo para ajudar a entender melhor esse conceito, nós vamos ligar uma Exchange a uma fila passando o seguintes argumentos ['format' => 'pdf', 'type' => 'file'] Nestes argumentos definimos duas chaves, uma format com o valor pdf e outra chave type com o valor file. Agora para conseguirmos enviar uma mensagem para a nossa fila precisamos informar no header da mensagem essas chaves exatamente com estes valores, se não a nossa mensagem não será entregue.
Existe uma chave especial no RabbitMQ para conseguirmos definir algumas regras no nosso roteamento, esta chave é a x-match Esta chave tem dois valores possíveis. Os valores são:
- x-match = all - Todos os valores no header precisar combinar(padrão).
- x-match = any - Pelo menos um valor no header precisar combinar.
Voltando ao exemplo anterior se modificarmos os argumentos passados na hora de fazer a ligação da Exchange com a fila para ['format' => 'pdf', 'type' => 'file', 'x-match' => 'any'] a regra muda. Agora se passarmos 'format' => 'pdf' ou 'type' => 'file' a mensagem vai ser enviada para fila já que pelo menos uma das chaves vai combinar.
Na prática.
Agora que entendemos como funciona uma Exchange do tipo headers vamos partir para o nosso exemplo prático. Vamos criar três filas: imgArchiver que vai armazenar qualquer tipo de imagem; imgJpg que vai fazer todo o processamento das imagem do tipo JPG e imgPng que vai fazer todo o processamento das imagem do tipo PNG. Nossa arquitetura de filas vai ficar da seguinte forma.
Vamos implementar o exemplo acima. Como sempre vamos criar um arquivo chamado sender.php e vamos fazer conexão com o RabbitMQ. Mas desta vez vamos precisar importar uma nova classe chamada AMQPTable ela será muito importante para trabalharmos com os argumentos e headers das mensagens. Vejamos um exemplo abaixo como vai ficar o nosso código.
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;
$host = 'localhost';
$porta = 5672;
$usuario = 'guest';
$senha = 'guest';
$connection = new AMQPStreamConnection($host, $porta, $usuario, $senha);
$channel = $connection->channel();
Agora vamos usar o método do exchange_declare para criar a nossa Exchange logs do tipo topic.
$nomeExchange = 'imagem';
$tipoExchange = 'headers';
$channel->exchange_declare($nomeExchange, $tipoExchange);
Agora vamos usar o método queue_declare para criar as três filas.
$channel->queue_declare('imgArchiver');
$channel->queue_declare('imgJpg');
$channel->queue_declare('imgPng');
Agora vamos fazer a ligação das nossas três filas com a Exchange, vamos usar a classe AMQPTable para criar um objeto $arguments que vai conter as nossas regras de roteamento. Preste atenção nos valores passados para cada ligação.
$queue = 'imgArchiver';
$exchange = 'imagem';
$routingKey = '';
$nowait = false;
$arguments = new AMQPTable(['type' => 'img', 'x-match' => 'any']);
$channel->queue_bind($queue, $exchange, $routingKey, $nowait, $arguments);
$queue = 'imgJpg';
$exchange = 'imagem';
$routingKey = '';
$nowait = false;
$arguments = new AMQPTable(['type' => 'img', 'ext' => 'jpg']);
$channel->queue_bind($queue, $exchange, $routingKey, $nowait, $arguments);
$queue = 'imgPng';
$exchange = 'imagem';
$routingKey = '';
$nowait = false;
$arguments = new AMQPTable(['type' => 'img', 'ext' => 'png']);
$channel->queue_bind($queue, $exchange, $routingKey, $nowait, $arguments);
Agora vamos enviar a nossa "imagem" do tipo JPG para a Exchange. Mais uma vez usamos a classe AMQPTable mas desta vez montamos um objeto $headers e usamos o método set do objeto $msg passando o objeto $headers como valor de uma chave chamada application_headers. Assim a nossa mensagem passa a possuir as chaves type e ext com os seus respectivos valores.
//IMAGEM JPG
$conteudo = 'conteudo em base64 da imagem jpg';
$msg = new AMQPMessage($conteudo);
$headers = new AMQPTable(['type' => 'img', 'ext' => 'jpg']);
$msg->set('application_headers', $headers);
Depois disso basta fazer a publicação.
$exchange = 'imagem';
$channel->basic_publish($msg, $exchange);
echo "Mensagem enviada: '" . $conteudo . "'\n";
Antes de executar o nosso código vamos fazer o mesmo processo para uma "imagem" do tipo PNG, o processo a basicamente o mesmo e o resultado vai ficar da seguinte forma.
//IMAGEM PNG
$conteudo = 'conteudo em base64 da imagem png';
$msg = new AMQPMessage($conteudo);
$headers = new AMQPTable(['type' => 'img', 'ext' => 'png']);
$msg->set('application_headers', $headers);
$exchange = 'imagem';
$channel->basic_publish($msg, $exchange);
echo "Mensagem enviada: '" . $conteudo . "'\n";
Fechamos a conexão.
$channel->close();
$connection->close();
E o resultado final no nosso arquivo sender.php é o seguinte.
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;
$host = 'localhost';
$porta = 5672;
$usuario = 'guest';
$senha = 'guest';
$connection = new AMQPStreamConnection($host, $porta, $usuario, $senha);
$channel = $connection->channel();
$nomeExchange = 'imagem';
$tipoExchange = 'headers';
$channel->exchange_declare($nomeExchange, $tipoExchange);
$channel->queue_declare('imgArchiver');
$channel->queue_declare('imgJpg');
$channel->queue_declare('imgPng');
$queue = 'imgArchiver';
$exchange = 'imagem';
$routingKey = '';
$nowait = false;
$arguments = new AMQPTable(['type' => 'img', 'x-match' => 'any']);
$channel->queue_bind($queue, $exchange, $routingKey, $nowait, $arguments);
$queue = 'imgJpg';
$exchange = 'imagem';
$routingKey = '';
$nowait = false;
$arguments = new AMQPTable(['type' => 'img', 'ext' => 'jpg']);
$channel->queue_bind($queue, $exchange, $routingKey, $nowait, $arguments);
$queue = 'imgPng';
$exchange = 'imagem';
$routingKey = '';
$nowait = false;
$arguments = new AMQPTable(['type' => 'img', 'ext' => 'png']);
$channel->queue_bind($queue, $exchange, $routingKey, $nowait, $arguments);
//IMAGEM JPG
$conteudo = 'conteudo em base64 da imagem jpg';
$msg = new AMQPMessage($conteudo);
$headers = new AMQPTable(['type' => 'img', 'ext' => 'jpg']);
$msg->set('application_headers', $headers);
$exchange = 'imagem';
$channel->basic_publish($msg, $exchange);
echo "Mensagem enviada: '" . $conteudo . "'\n";
//IMAGEM PNG
$conteudo = 'conteudo em base64 da imagem png';
$msg = new AMQPMessage($conteudo);
$headers = new AMQPTable(['type' => 'img', 'ext' => 'png']);
$msg->set('application_headers', $headers);
$exchange = 'imagem';
$channel->basic_publish($msg, $exchange);
echo "Mensagem enviada: '" . $conteudo . "'\n";
$channel->close();
$connection->close();
Agora se executarmos o nosso código vamos poder ver como ficou as nossas fila no RabbitMQ.
Como podemos ver a fila imgArchiver recebeu as duas mensagens já ela possui a regra de ['type' => 'img', 'x-match' => 'any'] fazendo com que batesse enviar no header a mensagem o 'type' => 'img' para que a mensagem fosse enviada para ela.
Da mesmo forma como no artigo anterior não vou dar exemplos de como fazer o consumo destas filas já que o processo é mesmo demonstrados nos primeiros artigos da série. Se está acompanhando não terá dificuldades. Mas em caso de dúvidas sinta-se a vontade para fazer qualquer pergunta nos comentários, terei prazer em ajudar.
Uma Exchange do tipo headers é menos usada do que as demais tipos mas é a mais complexa e flexível que o RabbitMQ oferece. Espero que esse pequeno exemplo possa ter te ajudado e entender o seu funcionamento e te instigue a pesquisar mais sobre as possibilidades deste tipo de Exchange.
Estamos chegando no final da série e na semana que vem teremos o último artigo com aspectos mais teóricos do RabbitMQ que são muitos importantes e que você vai precisar entender para tirar melhor proveito desde Message Broker. Espero ver você na semana que vem. Abraços.
Referências
https://www.rabbitmq.com/tutorials/amqp-concepts.html
https://github.com/php-amqplib/php-amqplib/tree/master/demo
https://www.tutlane.com/tutorial/rabbitmq/csharp-rabbitmq-headers-exchange