Como criar filas (queue) personalizadas no Magento 2
Tags: php, magento2, tutorial, braziliandevs
Published: 2022-06-03 17:53:54

## Contextualizando

### Como funciona a Fila?

As filas de mensagens fornecem um mecanismo de comunicação assíncrona no qual o publicador e os inscritos de uma mensagem não se comunicam, nem precisam se comunicar com a fila de mensagens ao mesmo tempo. Quando um remetente coloca uma mensagem em uma fila, ela é armazenada até que o destinatário a receba.

No Magento 2 (Adobe Commerce), o MQF (*Message Queue Framework*) é um sistema totalmente funcional que permite que um módulo publique mensagens em filas. Também cria consumidores para recebê-los de forma assíncrona.O MQF usa principalmente o RabbitMQ como agente de mensagens, que fornece uma plataforma escalável para enviar e receber mensagens. Também inclui um mecanismo para armazenar mensagens não entregues.

Um sistema básico de fila de mensagens também pode ser configurado sem usar o RabbitMQ. Neste sistema, um adaptador MySQL armazena mensagens no banco de dados. Existem três tabelas no banco de dados (`queue`, `queue_message` e `queue_message_status`) que gerenciam a *cron jobs* para as filas de mensagens.

### **RabbitMQ**

O RabbitMQ é um serviço de mensageria de código aberto que oferece um sistema de mensagens confiável, altamente disponível, escalável e portátil. O sistema de fila de mensagens deve ser estabelecido antes de instalar o Magento.

As filas de mensagens fornecem um mecanismo de comunicação assíncrona no qual o remetente e o destinatário de uma mensagem não se comunicam. Eles também não precisam se comunicar com a fila de mensagens ao mesmo tempo. Quando um remetente coloca uma mensagem em uma fila, ela é armazenada até que o destinatário as receba. Embora as mensagens fluam pelo RabbitMQ e seus aplicativos, elas só podem ser armazenadas dentro de uma fila. Uma fila é limitada apenas pelos limites de memória e disco do *host* (seria um grande buffer de mensagem). Muitos produtores podem enviar mensagens que vão para uma fila e muitos consumidores podem tentar receber dados de uma fila.

---

## Código para criar uma fila

### communication.xml

O arquivo `communication.xml` define aspectos do sistema da fila de mensagens que todos os tipos de comunicação possuem em comum, seguindo a estruturas de pastas `\{Vendor}\{Module}\etc\communication.xml.` Por hora, oferece suporte a conexões AMQP e de banco de dados.

```xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Communication/etc/communication.xsd">
    <topic name="{topic.name}" request="{Vendor}\{Module}\Api\Data\{ServiceName}"/>
    <topic name="{topic.name}" request="{request_type}" response="{response_type}">
        <handler name="{handlerName}" type="{Vendor}\{Module}\Model\{ServiceName}" method="{methodName}"/>
    </topic>
    <topic name="{topic.name}" schema="{Vendor}\{Module}\Api\{ServiceName}::{methodName}">
        <handler name="{handlerName}" type="{Vendor}\{Module}\Model\{ServiceName}" method="{methodName}"/>
    </topic>
</config>
```

#### Atributos do nó ‘topic’

A configuração de tópicos é flexível, pois é possível alternar a camada de transporte para tópicos no momento da implantação. Esses valores podem ser sobrescritos no arquivo `env.php`.

O atributo `name` é obrigatório. A definição do tópico deve incluir uma `request` ou um `schema`. É possível utilizar `schema` se desejar implementar uma interface de serviço personalizada. Caso contrário, especifique a `request`. Ao especificar o `request`, o `response` deve ser especificado, se o tópico for síncrono.

| Atributos | Descrição |
| --- | --- |
| name | Uma string que identifica exclusivamente o tópico. Um nome de tópico deve ser uma série de strings separadas por pontos. A string mais à esquerda deve ser a mais geral, e cada string posterior deve restringir o escopo. |
| request | Especifica o tipo de dados do tópico. |
| response | Especifica o formato da resposta. Esse parâmetro é necessário se estiver definindo um tópico síncrono, caso o tópico seja assíncrono basta omita este parâmetro. |
| schema | A interface que descreve a estrutura da mensagem (o formato deve ser: `\{Vendor}\{Module}\Api\{ServiceName}::{methodName}`). |

#### Atributos do nó ‘handler’

O nó `<handler>` especifica a classe onde existe a lógica para manipulação de mensagens e o método que a executa.

| Atributos | Descrição |
| --- | --- |
| name | String que define o nome do nó `<handler>`.  |
| type | A classe ou interface que define o `<handler>`. |
| method | O nome do método que será executado. |
| disabled | Determina se o `<handler>` está desabilitado. O valor padrão é `false`. |

### queue_consumer.xml

Este arquivo define aspectos do sistema de fila de mensagens que todos os tipos de comunicação têm em comum e deve estar localizado na pasta `\{Vendor}\{Module}\etc\queue_consumer.xml`, e pode possuir mais de um consumidor.

```xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/consumer.xsd">
    <consumer name="{ConsumerName}" queue="{queue_name}" handler="{Vendor}\{Module}\Model\{HandlerName}::{methodName}" connection="{amqp/db}" maxMessages="{integer}" maxIdleTime="{integer}" sleep="{integer}" onlySpawnWhenMessageAvailable="{0/1}"/>
    <consumer name="{ConsumerName}" queue="{queue_name}" consumerInstance="{Vendor}\{Module}\Model\{ConsumerName}" connection="{amqp/db}" maxMessages="{integer}" maxIdleTime="{integer}" sleep="{integer}" onlySpawnWhenMessageAvailable="{0/1}"/>
</config>
```

#### Atributos do nó ‘consumer’

Os atributos `maxIdleTime` e `sleep` serão tratados apenas por consumidores que foram enviados com o parâmetro `maxMessages` definido. O atributo `onlySpawnWhenMessageAvailable` é verificado e validado apenas pela classe `\Magento\MessageQueue\Model\Cron\ConsumersRunner` que executa processos de consumidor com cron.

| Atributos | Descrição |
| --- | --- |
| name | Nome do nó `<consumer>`. |
| queue | Define o nome da fila para a qual será enviada a mensagem. |
| handler | Especifica a classe ou interface e o método que processa a mensagem. O valor deve ser especificado no formato `\{Vendor}\{Module}\{Directory}\{HandlerName}:{methodName}`. |
| consumerInstance | O nome da classe que consumirá a mensagem |
| connection | `amqp`: o nome da conexão deve corresponder ao atributo de conexão no arquivo `queue_topology.xml`. `db`: qualquer outro cenário. |
| maxMessages | Especifica o número máximo de mensagens a serem consumidas. |
| maxIdleTime | Define o tempo máximo de espera em segundos para uma nova mensagem da fila. Se nenhuma mensagem for tratada nesse período de tempo, o consumidor enviará. O valor padrão é `null`. |
| sleep | Especifica o tempo em segundos antes de verificar se uma nova mensagem está disponível na fila. O valor padrão é `null`, o que equivale a 1 segundo. |
| onlySpawnWhenMessageAvailable | Identifica se um consumidor deve ser gerado somente se houver mensagem disponível na fila relacionada. O valor padrão é `null`. |

#### Consumidor Handler

Um *handler* é uma classe com um método que consome uma mensagem. E pode ser definido no nó `<topic>`, como um nó filho `<handler>` no arquivo `communication.xml`, ou como atributo `handler` no nó `<consumer>` no arquivo `queue_consumer.xml`.

Se o consumidor em `queue_consumer.xml` não tiver um `consumerInstance` definido, o sistema usará o consumidor padrão `\Magento\Framework\MessageQueue\Consumer`. Neste caso, caso o nó `<consumer>` conter o atributo `handler`, ele será usado, e o nó `<handler>` no arquivo `communication.xml` será ignorado.

Se o consumidor no arquivo `queue_consumer.xml` tiver um `consumerInstance` definido, então a implementação do consumidor será definido como o atributo `handler` será utilizado.

### queue_topology.xml

O arquivo `queue_topology.xml` define as regras de roteamento de mensagens e declara filas e trocas, seguindo a estruturas de pastas `\{Vendor}\{Module}\etc\queue_topology.xml.`

```xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/topology.xsd">
    <exchange name="{exchange.name}" type="topic" connection="{amqp/db}" durable="{true/false}" autoDelete="{true/false}">
        <binding id="{queue_consumer_name}" topic="{communication.topic.name}" destinationType="queue" destination="{queue_consumer_queue}">
            <arguments>
                <argument name="{argument_name}" xsi:type="string">{value}</argument>
            </arguments>
        </binding>
    </exchange>
</config>
```

#### Atributos do nó ‘exchange’

| Atributos | Descrição |
| --- | --- |
| name | Define o identificador único. |
| type | Especifica o tipo de troca. Deve ser `topic`. |
| connection | Para conexões AMQP, o nome da conexão é recomendável que se utilize `amqp`. Para conexões MySQL, o nome da conexão deve ser `db`. |
| durable | Indica se a `exchange` é persistente. As `exchanges` não duráveis são eliminadas quando o servidor é reiniciado. O padrão é `true`. |
| autoDelete | Indica se a `exchange` é excluída quando todas as filas terminarem de usá-la. O padrão é `false`. |
| internal | Se definido como verdadeiro, a `exchange` não pode ser usada diretamente pelos _publishers_, mas somente quando vinculada a outras `exchanges`. O padrão é `false`. |

#### Atributos do nó ‘binding’

O nó `<binding>` é um nó filho do nó `<exchange>`.

| Atributos | Descrição |
| --- | --- |
| id | Define o identificador único (recomendável colocar o mesmo valor que está atribuído ao atributo `name` do arquivo `queue_consumer.xml`) . |
| topic | O nome de um tópico (colocar o mesmo valor que está atribuído ao atributo name do arquivo communication.xml). |
| destinationType | Deve ser `queue`. |
| destination | Identifica o nome de uma fila (recomendável colocar o mesmo valor que está atribuído ao atributo `queue` do arquivo `queue_consumer.xml`). |
| autoDeletedisabled | Determina se o `<binding>` está desabilitado. O valor padrão é `false`. |

### **queue_publisher.xml**

O arquivo `queue_publisher.xml` define qual conexão e troca usar para publicar mensagens para um tópico específico, seguindo a estruturas de pastas `\{Vendor}\{Module}\etc\queue_publisher.xml.`

```xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework-message-queue:etc/publisher.xsd">
    <publisher topic="{communication.topic.name}">
        <connection name="{amqp/db}" exchange="{exchange.name}" disabled="{true/false}"/>
    </publisher>
</config>
```

#### Atributos do nó ‘publisher’

| Atributos | Descrição |
| --- | --- |
| topic (obrigatório) | O nome de um tópico (colocar o mesmo valor que está atribuído ao atributo `name` do arquivo `communication.xml`). |
| disabled | Determina se esta fila está desabilitada. O valor padrão é `false`. |

#### Atributos do nó ‘connection’

O nó `<connection>` é um nó filho do elemento `<publisher>`. Não deve haver mais de uma `<connection>` habilitada para um `<publisher>`. Caso o nó `<connection>` esteja omitido o elemento de conexão, a conexão padrão amqp do magento será usada no atributo `exchange`.

| Atributos | Descrição |
| --- | --- |
| name (obrigatório) | `amqp`: o nome da conexão deve corresponder ao atributo de conexão no arquivo `queue_topology.xml`. `db`: qualquer outro cenário. |
| exchange | O nome da `exchange` na qual vai publicar a mensagem (colocar o mesmo valor que está atribuído ao atributo `name` do nó `<exchange>` do arquivo `queue_topology.xml`). O nome da `exchange` do sistema padrão é `magento`. |
| disabled | Determina se esta fila está desabilitada. O valor padrão é `false`. |

---

## Finalizando

> Valores entre chaves (`{test}`) devem ser alterados na implementação do código.

### Habilitando as alterações

Apague os arquivos que são gerados na compilação do Magento e execute o comando PHP para gerar a configuração das injeções de dependência e todas as classes ausentes que precisam ser geradas (proxys, interceptors, etc) e para limpar todos os caches de armazenamento em cache do processos.

```bash
rm -rf var/generation/
rm -rf generated/
php bin/magento setup:di:compile
php bin/magento cache:clean
php bin/magento flush
```

### Diretórios e Arquivos

Segue a a lista de diretórios e arquivos que devem ser criados.

```
- app/
  - code/
    - {Vendor}/
        - {Module}/
          - etc/
            - communication.xml
            - queue_consumer.xml
            - queue_topology.xml
            - queue_publisher.xml
            - module.xml
          - registration.php
          - composer.json
```