Introduction to RabbitMQ and Symfony
One day I was trying to learn the deep concepts of RabbitMQ, its use cases and why it’s different from other message brokers. I’ve started by reading the cool documentation and then I was eager to try it out in a demo application.
Turns out it wasn’t so easy to setup a Symfony application and connect to RabbitMQ. Google displayed different solutions and I also needed StackOverflow to install some additional dependencies.
Hopefully I could condense all the information and display it here in a simple and fun way.
What am I going to build?
Initially I thought of creating a web application to explore the different patterns of using RabbitMQ. After struggling with the setup of RabbitMQ using a popular web framework, I decided to take a step back and simply create an endpoint that publishes a message, and a consumer that logs the content received. So let’s build it!
But why Symfony, right? It’s a popular PHP framework and I really liked its software architecture and integration with RabbitMQ.
First, I installed Symfony CLI and create a a traditional web application:
symfony new --full php-symfony-rabbitmq
My application now can be started by running the following command on the new project directory:
Symfony Messenger is the message bus abstraction provided by a separate lib. Let’s install it then!
composer require symfony/messenger
Following the good documentation, I created a simple class to encapsulate the message to be published:
final class SampleMessage
public function __construct(private string $content)
} public function getContent(): string
And its respective 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!');
Now to see if everything is working so far, I added a simple endpoint to dispatch a message:
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()));
Thankfully when I hit the endpoint, I can see the output from the handler and the http response from the controller:
curl http://localhost:8000/sampleHandler handled the message!Message with content content was published
Where is RabbitMQ?
Yeah so far there’s not even a trace of RabbitMQ, but don’t worry because I’ve prepared the terrain.
Install all dependencies
I’ll use docker to spawn a RabbitMQ instance, because it’s much easier. Just install Docker Compose and then edit the
.docker-compose.yml file on the project root directory to add a new service:
docker-compose up on project root directory, I can see everything is working.
PECL AMQP Extension
The AMQP (Advanced Message Queuing Protocol, the protocol which RabbitMQ uses) extension is needed to be installed using PECL (PHP Extension Community Language). This was a bit tricky, at least on MacOS:
First, installed HomeBrew:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install rabbitmq-c
Which enabled the installation of amqp extension:
pecl install amqp
When prompted to enter the path to
librabbitmq, you need to check which version is installed inside the folder
/usr/local/Cellar/rabbitmq-c/. Mine was
Set the path to librabbitmq install prefix [autodetect] : /usr/local/Cellar/rabbitmq-c/0.11.0
Finally, the last dependency:
composer require symfony/amqp-messenger
What a relief! Now I can go back to proper coding.
Using the asynchronous power
By default the username and password created in the docker image are
guest, which is coincidentally the exact line I need to uncomment on
.env file to expose the RabbitMQ connection as an environment variable:
###> symfony/messenger ###
# Choose one of the transports below
###< symfony/messenger ###
Now with this new known value, I need to tell the application which messages should be handled by this new transport.
Then on file
config/packages/messanger.yaml, I defined a new transport and the message type that will use it:
async: '%env(MESSENGER_TRANSPORT_DSN)%' routing:
# Route your messages to the transports
Now I can hit the previous endpoint again:
And in another terminal I can check the message is still being handled:
php bin/console messenger:consume async -vv
This outputs a verbose log message but the important parts are:
[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
To be sure the application is really using RabbitMQ, I can access the admin on http://localhost:15672 and see this cool chart:
Finally I have a basic setup of RabbitMQ and Symfony! Now I can give life to a bunch of side projects and explore message queueing patterns. Hope you enjoyed and learned something in the way!