Use Symfony2 and rabbitmq as a microservice

Use Symfony2 and rabbitmq as a microservice

How to use Symfony2 and rabbitmq as a microservice? Rabbitmq is very powerful tool. It could act as messaging system, system written in many languages or core for our microservice based application. Microservice – what is it?

What are microservices?

In computing, microservices is a software architecture style in which complex applications are composed of small, independent processes. These processes communicate with each other using language-agnostic APIs. These services are small, highly decoupled and focus on a small task.

If you need more information about microservice architecture, check out Martin Fowler’s blog post about it
http://martinfowler.com/articles/microservices.html

How to bring microservice architecture to Symfony2

Let’s design game based application like Ogame or Ikariam, where you have to wait certain amount of time to build a building or attack somebody.

I will show you how to “send our soldiers to academy for training”. We will be using Redis to store our “soldiers training” result, Symfony2 to handle requests, and AngularJS to frontend actions.

Let’s build!

Lets add 3 new bundles to composer.json

"oldsound/rabbitmq-bundle": "1.*",
"snc/redis-bundle": "1.1.x-dev",
"predis/predis": "0.8.x-dev"

Don’t forget to register namespaces in Symfony2 AppKernel.php

After that, add new method to our controller SoldiersController.php

[sourcecode language=”php” wraplines=”false” collapse=”false”]
public function addSoldierToQueueAction(Request $request)
{
$data = json_decode($request->getContent(), false);
$this-&gt;get(‘consumer’)-&gt;process($this-&gt;getUser()-&gt;getId(), $data-&gt;time, $data-&gt;range, $data-&gt;amount);</p>
<p style=”text-align: justify;”>return new JsonResponse(‘ok’);
}
[/sourcecode]

In the front-end we have buttons that send some data to our controller. In this case we’re sending: time (how much time we need to train one soldier), range (soldier range), amount (how many soldiers we would like to train).

soldiers-factory.js

[sourcecode language=”javascript” wraplines=”false” collapse=”false”]
function addToQueue(params) {
return $http.post(BASE_END_POINT + ‘/soldiers/queue’, params);
}
[/sourcecode]

Use Symfony2 and rabbitmq as a microservice

About time

Now, we have our front-end sending information on how many soldiers we would like to train and we have a controller that getting this data. It’s time to write a service to produce our request to consumer. Why do we need this? Because the idea of browser based games is to wait a given time for some action. In this case, wait 5 seconds for every next trained soldier. Why do we need the consumer? Because if we close our browser and reopen it after some time we expect that our soldiers will be trained. And we do not want to lose our requests.

ProducerService.php

[sourcecode language=”php” wraplines=”false” collapse=”false”]
class Producer
{
/** @var Producer $producer */
private $producer;</p>
<p style=”text-align: justify;”>public function __construct($producer)
{
$this-&gt;producer = $producer;
}</p>
<p style=”text-align: justify;”>public function process($userId, $time, $range, $amount)
{
$data = [
‘user’ =&gt; $userId,
‘time’ =&gt; $time,
‘range’ =&gt; $range,
‘amount’ =&gt; $amount
];</p>
<p style=”text-align: justify;”>$this-&gt;producer-&gt;publish(json_encode($data));
}
}
[/sourcecode]

This class above is getting our request from controller. Now we should be sending the signal to our consumer.

ConsumerClass.php

[sourcecode language=”php” wraplines=”false” collapse=”false”]
class Consumer implements ConsumerInterface
{
/**
* @var Client $redis
*/
private $redis;</p>
<p style=”text-align: justify;”>/**
* @param EntityManager $em
* @param Client $redis
* @param Container $container
*/
function __construct(EntityManager $em, Client $redis, Container $container)
{
$this-&gt;em = $em;
$this-&gt;redis = $redis;
$this-&gt;container = $container;
}</p>
<p style=”text-align: justify;”>/**
* @param AMQPMessage $msg
* @return mixed
*/
public function execute(AMQPMessage $msg)
{
$json = json_decode($msg-&gt;body);
for ($i = 1; $i &lt;= $json-&gt;amount; $i++) {
sleep($json-&gt;time / $json-&gt;amount);
$this-&gt;redis-&gt;hincrby($json-&gt;user, $json-&gt;range, 1);
}
}
}
[/sourcecode]

As you can see, at the end of the file we’re sending a request to Redis, to increment amount of given soldier by 1.

[sourcecode language=”php” wraplines=”false” collapse=”false”]
$this-&gt;redis-&gt;hincrby($json-&gt;user, $json-&gt;range, 1);
[/sourcecode]

Now that everything is configured…

…you need to add information to your services.yml destination of your consumer

    consumers:
        consumer:
            connection:       default
            exchange_options: {name: 'file_nodes', type: direct}
            queue_options:    {name: 'file_nodes'}
            callback:         process_node

And simply start it in the terminal:

php app/console rabbitmq:consumer consumer

How does it look if we open Redis console?

Redis console

Let’s check our Rabbitmq dashboard to manage our requests.

Rabbitmq dashboard

Now, every time period that we have declared somewhere our soldier is trained, and new army is ready to fight with another user army.

redis

Every time when you refresh your browser you’re getting new data from our Redis database. Because results are stored immediately, in real-time we see that we have more trained soldiers.
This example shows only a simple solution on how to write a browser based game. Soon, I’ll try to show how to add real-time actions using node.js

author: Sebastian Superczyński