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 fresh Symfony 4.3 project with RabbitMQ and show few examples of using them in distributing payloads to different users — messaging application.
Why RabbitMQ?
- Reliability — Offering a variety of features including 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 offers 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 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 differently.
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 offers 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 setup 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
{
}
Sending your first message!
Lets 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 empty body mentioned above. Also we created the createMessage with Faker Factory used for mocking up our data depending on our . 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 differently 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 of 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/rabbitmq