DEV Community

Cover image for Introdução ao RabbitMQ e Symfony
Fabio Hiroki for Textos em Português

Posted on • Edited on

3 1

Introdução ao RabbitMQ e Symfony

Um dia eu estava tentando aprender os conceitos avançados do RabbitMQ, os casos de uso e as diferenças para os outros message brokers. Comecei lendo a boa documentação e então eu estava ansioso para experimentar em uma aplicação real.

Entretanto não foi tão simples de criar uma aplicação Symfony e conectar com o RabbitMQ. Os resultados da busca do Google não eram tão precisos e eu ainda precisei da ajuda do StackOverflow para instalar dependências adicionais.

Espero que eu tenha conseguido condensar toda a informação aqui e mostrá-la de uma forma simples e divertida.

O que eu vou construir?

Um homem confuso
Inicialmente eu pensei em criar uma aplicação web para explorar os diferentes padrões do RabbitMQ. Depois de sofrer com a integração do RabbitMQ com framework web popular, eu decidi dar um passo para trás e simplesmente só criar um endpoint que publica uma mensagem, e um consumidor que loga o conteúdo recebido. Então vamos construir!

Código final no github:


Symfony

Mas por que Symfony? É um framework PHP popular e no final eu gostei de como ficou a integração e a arquitetura com o RabbitMQ.

Primeiramente eu instalei o Symfony CLI e através dele, criei uma aplicação web tradicional:

symfony new --full php-symfony-rabbitmq
Enter fullscreen mode Exit fullscreen mode

Posso rodar minha aplicação através do seguinte comando no diretório do projeto:

symfony serve
Enter fullscreen mode Exit fullscreen mode

Symfony messenger

Um carteiro mostrando uma carta
Symfony Messenger é a abstração de mensageria provido por uma dependência separada. Vamos instalá-la!

composer require symfony/messenger
Enter fullscreen mode Exit fullscreen mode

Seguindo a boa documentação, eu criei uma classe simples para encapsular a mensagem a ser publicada:

final class SampleMessage
{
    public function __construct(private string $content)
    {
    }

    public function getContent(): string
    {
        return $this->content;
    }
}
Enter fullscreen mode Exit fullscreen mode

E a sua respectiva classe handler:

final class SampleMessangeHandler implements MessageHandlerInterface
{
    public function __invoke(SampleMessage $message)
    {
        // magically invoked when an instance of SampleMessage is dispatched
        print_r('Handler handled the message!');
    }
}
Enter fullscreen mode Exit fullscreen mode

Agora para verificar que tudo está funcionando até agora, eu adicionei um endpoint simples para enviar uma mensagem:

final class SampleController extends AbstractController
{
    #[Route('/sample', name: 'sample')]
    public function sample(MessageBusInterface $bus): Response
    {
        $message = new SampleMessage('content');
        $bus->dispatch($message);

        return new Response(sprintf('Message with content %s was published', $message->getContent()));
    }
}
Enter fullscreen mode Exit fullscreen mode

Ao testar o endpoint, verifico que a saída impressa pelo handler e pela resposta http está correta:

curl http://localhost:8000/sample

Handler handled the message!Message with content content was published
Enter fullscreen mode Exit fullscreen mode

Onde está o RabbitMQ?

Gif de um coelho

Até agora não temos nem um sinal do RabbitMQ, mas não se preocupe porque deixei todo o terreno preparado para introduzi-lo.

Instalação das dependências

Imagem docker

Vou utilizar o docker para subir uma instância do RabbitMQ porque é muito fácil dessa forma. Só é preciso instalar o Docker Compose e então editar o arquivo .docker-compose.yml no diretório do projeto PHP para adicionar um novo serviço:

version: '3'

services:
  rabbitmq:
    image: rabbitmq:3.9-management
    ports:
      - '5672:5672'
      - '15672:15672'
Enter fullscreen mode Exit fullscreen mode

Ao rodar docker-compose up é possível verificar que a instância do RabbitMQ está rodando.

Extensão PECL AMQP

AMQP (Advanced Message Queuing Protocol), é o protocolo que sustenta o RabbitMQ. A instalação da sua extensão utilizando PECL (PHP Extension Community Language) foi um pouco difícil, pelo menos no MacOS:

Primeiro, instalei o HomeBrew:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Enter fullscreen mode Exit fullscreen mode

Depois instalei rabbitmq-c:

brew install rabbitmq-c
Enter fullscreen mode Exit fullscreen mode

O que por fim permitiu a instalação da extensão:

pecl install amqp
Enter fullscreen mode Exit fullscreen mode

Quando foi pedido para digitar o caminho para librabbitmq, você precisa verificar qual é a versão instalada dentro do diretório /usr/local/Cellar/rabbitmq-c/. A minha versão era a 0.11.0:

Set the path to librabbitmq install prefix [autodetect] : /usr/local/Cellar/rabbitmq-c/0.11.0
Enter fullscreen mode Exit fullscreen mode

Finalmente a última dependência:

composer require symfony/amqp-messenger 
Enter fullscreen mode Exit fullscreen mode

Que alívio! Agora posso voltar a programar.

Utilizando o poder da assincronicidade

Por padrão, o usuário e a senha criados na imagem docker são guest, o que coincidentemente é a linha exata que eu preciso descomentar no arquivo .env, que expõe a conexão ao RabbitMQ como uma variável de ambiente:

###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=doctrine://default
 MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
###< symfony/messenger ###
Enter fullscreen mode Exit fullscreen mode

Agora com essa nova variável, eu preciso dizer a aplicação quais mensagens deveriam ser tratadas por esse transporte.

Então no arquivo config/packages/messanger.yaml eu defino um novo transport e o tipo de mensagem a ser tratada:

framework:
    messenger:
        transports:
            # https://symfony.com/doc/current/messenger.html#transport-configuration
            async: '%env(MESSENGER_TRANSPORT_DSN)%'

        routing:
            # Route your messages to the transports
            'App\Message\SampleMessage': async
Enter fullscreen mode Exit fullscreen mode

Nesse ponto eu posso testar o endpoint novamente:

curl http://localhost:8000/sample
Enter fullscreen mode Exit fullscreen mode

E em outro terminal eu ligo o consumidor:

php bin/console messenger:consume async -vv
Enter fullscreen mode Exit fullscreen mode

Esse comando imprime várias mensagens bem verbosas, mas as partes importantes são:
:

[messenger] Received message App\Message\SampleMessage
[messenger] App\Message\SampleMessage was handled successfully (acknowledging to transport).
[messenger] Message App\Message\SampleMessage handled by App\MessageHandler\SampleMessangeHandler
Enter fullscreen mode Exit fullscreen mode

E para ter certeza que a aplicação está realmente usando o RabbitMQ, eu posso acessar o admin no endereço http://localhost:15672 e verificar esse gráfico bonito:
Gráfico mostrando as mensagens enviadas

Conclusão

Gif de alguém feliz
Finalmente eu tenho um setup básico do RabbitMQ e Symfony! Agora eu posso dar vida a vários side projects e explorar os padrões de mensageria. Espero que tenha gostado e também aprendido algo!

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (2)

Collapse
 
totmal profile image
Joao Paulo

Cara incrivel a simplicidade que vc descreveu, mas confesso que não funcionou aqui! kkkk

Vc tem esse código em GIT para que eu possa verificar a estrutura final do seu projeto?

Collapse
 
fabiothiroki profile image
Fabio Hiroki

Ops esqueci de adicionar o link para o github: github.com/fabiothiroki/php-symfon...

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay