Symfony and RabbitMQ

For boosting up the performances of your web applications to the maximum we often need to use some sort of queues to perform time-consuming tasks. One of the best examples for doing that is RabbitMQ.

RabbitMQ is lightweight and easy to deploy on-premises and in the cloud. It supports multiple messaging protocols.

In this short tutorial, we will be setting up a fresh Symfony 4.3 project with RabbitMQ and show a few examples of using them in distributing payloads to dierent users — messaging application.

Why RabbitMQ?

  • Reliability — Oering a variety of features including a performance with minimum loss and persistence.
  • Flexible Routing — Before messages come to the queue they are routed through exchanges. Exchanges are used for typical routing logic.
  • Clustering — Several RabbitMQ servers on a local network can be clustered together, forming a single logical broker.
  • Federation — For servers that need to be more loosely and unreliably connected than clustering allows, RabbitMQ oers a federation model.
  • Multi-protocol — Variety of messaging protocols — AMQP 0–9–1, STOMP, MQTT, AMQP 1.0, HTTP and WebSockets.

Requirements

  • Symfony 4.3
  • RabbitMQ Bundle
  • Symfony Annotations
  • Faker
  • Rabbit Server

Getting Started

To get you started to create a new Symfony 4.3 project using Symfony command in the terminal:

symfony new project-name

or with the PHP dependency manager Composer:

composer create-project symfony/website-skeleton project-name

Inside your newly created Symfony project open up the terminal and install the RabbitMQ bundle using this command:

composer require php-amqplib/rabbitmq-bundle

This will also create a folder called “Consumer” which we can delete since we will be organizing our folders dierently.

For easier route defining Symfony allows you to use annotation routes instead of defining them in YAML file:

composer require annotations

And Faker for easy creation of mocked data for our messages:

composer require fzaninotto/faker

We need a RabbitMQ server now. We will set it up locally (don’t worry its easy). For MacOS we will use homebrew.

brew update
brew install rabbitmq
export PATH=$PATH:/usr/local/opt/rabbitmq/sbin
rabbitmq-server (to start the server)

Your RabbitMQ dashboard is available at:

http:// http://localhost:15672/

You can access your dashboard with username and password “guest”.

In the folder, you will find an environment file .env with already automatically added variable for connecting to the localhost RabbitMQ server.

###> php-amqplib/rabbitmq-bundle ###
RABBITMQ_URL=amqp://guest:guest@localhost:5672
###< php-amqplib/rabbitmq-bundle ###

Automatically when RabbitMQ bundle was installed you will also get a configuration file stored in /config/packages/ called old_sound_rabbit_mq.yaml

old_sound_rabbit_mq:
connections:
default:
url: '%env(RABBITMQ_URL)%'
read_write_timeout: 4
heartbeat: 2
producers:
messaging:
connection: default
exchange_options: { name: 'qMessages', type: direct }
consumers:
messaging:
connection: default
exchange_options: { name: 'qMessages', type: direct }
queue_options: { name: 'qMessages'}
callback: message_service
enable_logger: true
  • producer — Sending the message to the broker.
  • consumer — Receiving the messages.
  • heartbeat — AMQP 0–9–1 oers a heartbeat feature to ensure that the application layer promptly finds out about disrupted connections (and also completely unresponsive peers). Heartbeats also defend against certain network equipment which may terminate “idle” TCP connections.

There are also four types of exchange options we can give to our configuration file:

  • direct — sent directly to the written queue
  • fanout — to all the queues
  • headers — sent to queue based on the headers property
  • topic — sent to one or more queues based on a correspondence between the routing key and the routing pattern

Now we need to set up the “qMessages” exchange on our Rabbit server. We can do that with the following command

php bin/console rabbitmq:setup-fabric

Your RabbitMQ Management Dashboard should look like this now.

The only thing left to do before we can send our message is binding our producer service to a class.

In old_sound_rabbit_mq.yml file add “class” to the producer like this:

producers:
messaging:
class: App\Rabbit\MessagingProducer
connection: default
exchange_options: { name: 'qMessages', type: direct }

In your services.yaml we need to register both Consumer and Producer:

message_service:
class: App\Rabbit\MessageConsumer

App\Rabbit\MessagingProducer: '@old_sound_rabbit_mq.messaging_producer'

Now create a new folder called Rabbit and inside create a new class called MessagingProducer with an empty body. We will keep this very simple, so our class looks like this:

<!--?php

namespace App\Rabbit;

use Symfony\Component\HttpFoundation\JsonResponse;

class MessagingProducer extends \OldSound\RabbitMqBundle\RabbitMq\Producer
{
}</pre-->

Sending your first message!

Let’s start by making a MessageService class in our service folder.

<!--?php

namespace App\Service;

use App\Rabbit\MessagingProducer;
use Faker\Factory;
use Symfony\Component\HttpFoundation\JsonResponse;

class MessageService
{
private $messagingProducer;

public function __construct(MessagingProducer $messagingProducer)
{
$this--->messagingProducer = $messagingProducer;
}

public function createMessage(int $numberOfUsers): JsonResponse
{
$faker = Factory::create();

for ($i=0; $i<$numberOfUsers; $i++) {
$message = json_encode([
'sender' => $faker->companyEmail,
'receiver' => $faker->email,
'message' => $faker->text,
]);

$this->messagingProducer->publish($message);
}

return new JsonResponse(['status' => 'Sent!']);
}
}

We have injected our MessagingProducer class with the empty body mentioned above. Also, we created the createMessage with Faker Factory used for mocking up our data depending on our. A simple function for creating our messages that will be sent to the queue.

Now we can make our MessageController in the Controller folder and add a function called sendMessage. With the annotation add the route with the slug “numberOfMessages” in the URL.

<!--?php

namespace App\Controller;

use App\Service\MessageService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;

class MessageController
{
private $messageService;

public function __construct(MessageService $messageService)
{
$this--->messageService = $messageService;
}

/**
* @Route("/send-message/{numberOfMessages}", name="send_message", methods={"GET"})
*/
public function createMessages($numberOfMessages): JsonResponse
{
$this->messageService->createMessage($numberOfMessages);

return new JsonResponse(['status' => 'Sent!']);
}
}

In terminal write the command:

symfony server:start

and now just go to the link the server provided you:

http://127.0.0.1:8000/send-message/2

And voila! You have successfully sent two random generated messages to your queue!

Let’s Consume our messages!

When I say Consume, I mean we actually want to collect them from our Rabbit queue and process them the way we want. Here we will define a simple class for collecting and showing our messages from the queue.

<!--?php

namespace App\Rabbit;

use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
use PhpAmqpLib\Message\AMQPMessage;

class MessageConsumer implements ConsumerInterface
{

public function execute(AMQPMessage $msg)
{
$message = json_decode($msg--->body, true);

echo 'Received a message from '.$message['sender'];
}
}

Now we need to run the command for initiating our function for collecting and displaying messages in our terminal:

php bin/console rabbitmq:consumer messaging

In your terminal, all the messages that are displayed are automatically deleted from the queue unless you define them dierently with “durable” property. Now you can start being artistic and try out something else with the consumed messages.

For example, saving the consumed messages to the database. Consume half a million messages and time the execution of your script — You will be surprised, or simply just start by using RabbitMQ in your applications.

For full source code visit — https://github.com/LukaGado/ra…


Leave a Reply

Your email address will not be published. Required fields are marked *