RabbitMQ

Utiliser

LIKE A BOSS

VINCENT CHALAMON

Rabbit aime quoi ?

Gestionnaire de files de messages

  • Advanced Message Queuing Protocol
     
  • Asynchrone
     
  • PHP, Node.JS, Java, Ruby, Python…

Source image : http://www.inspirel.com/

VOUS AVEZ DIT ASYNCHRONE ?

Installation

  • Debian, OS X, Windows
     
  • brew, .tar.gz, .deb, .rpm, .exe, .zip, .npm  

docker run -d rabbitmq:3-management

Lancement/arrêt du service

rabbitmq-server -detached

rabbitmqctl stop

Interface de gestion

  • Vue sur le trafic

  • Gérer les listes (exchanges/queues)

  • Administrer les utilisateurs

  • Visualiser les connexions

Exchange/queue : kezako ?

  • Exchange : liste de publication de messages
     

  • Queue : liste de lecture des messages envoyés depuis une exchange

Vous comprenez ce qu'il dit vous ?

  • Soumission d’un message sur une exchange

  • Lecture d’un message depuis une queue

Types d'exchange

  • Fanout : copie le message dans chaque queue
     

  • Direct : transmet le message à 1 queue
     

  • Topic : transmet le message à N queues selon un filtre

Mais alors fanout = topic, non ?

Non : “selon un filtre” (routing key)

Et… c'est tout ?

  • Vhost : encapsulation des exchanges et queues dans un domaine
     

  • Lazy : services instanciés à la demande
     

  • RPC : Remote Procedure Call (ATTENTION : perte d’asynchrone !)

Une api Symfony 2

Installation

composer require videlalvaro/rabbitmqbundle

Définition de la connexion au service RabbitMQ :

  • authentification
  • hôte
  • port
  • domaine…
old_sound_rabbit_mq:
    connections:
        default:
            host:     localhost
            port:     5672
            user:     guest
            password: guest
            vhost:    /
            lazy:     false

Configuration de la connexion

Comme une lettre à la poste !

Création de l'exchange
VIA L'interface

Utilisation du producer par défaut

old_sound_rabbit_mq:
    producers:
        foo:
            connection: default
            exchange_options:
                name: 'foo_exchange'
                type: topic

old_sound_rabbit_mq.<name>_producer

old_sound_rabbit_mq.foo_producer

La lettre au père noël

<service id="les_tilleuls_demo.form.handler" class="FooFormHandler">
    ...
    <argument type="service" id="old_sound_rabbit_mq.foo_producer" />
</service>
$this->producer->publish(serialize($entity));

Serialization par défaut : serialize

J'voulais écrire Des mots d'amour !

Création de la queue
via l'interface

Création d'un consumer

old_sound_rabbit_mq:
    consumers:
        foo:
            connection: default
            exchange_options:
                name: 'foo_exchange'
                type: topic
            queue_options:
                name: 'foo_queue'
            callback: les_tilleuls_demo.consumer.foo
<?php

namespace LesTilleuls\DemoBundle\Component\AMPQ;

use LesTilleuls\DemoBundle\Entity\Foo;
use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
use PhpAmqpLib\Message\AMQPMessage;

class FooConsumer implements ConsumerInterface
{
    /**
     * {@inheritdoc}
     */
    public function execute(AMQPMessage $msg)
    {
        /** @var Foo $foo */
        $foo = unserialize($msg->body);
        echo "foo ".$foo->getName()." successfully downloaded!\n";
    }
}

Consommation du message

php app/console rabbitmq:consumer <name>

php app/console rabbitmq:consumer <name> -m 50

Message supprimé de la queue

RPC or not RPC ?

Zat iz zeuh kwestion !

La réponse du père noël

public function getAndReplyAction(Foo $foo)
{
    $client = $this->get('old_sound_rabbit_mq.foo_rpc');
    $client->addRequest(serialize($foo), 'foo_exchange',
        'request_id');
    $replies = $client->getReplies();

    return array_shift($replies);
}
<?php

namespace LesTilleuls\DemoBundle\Component\AMPQ;

use LesTilleuls\DemoBundle\Entity\Foo;
use OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Message\AMQPMessage;

class FooRpcConsumer implements ConsumerInterface
{
    /**
     * {@inheritdoc}
     */
    public function execute(AMQPMessage $msg)
    {
        /** @var Foo $foo */
        $foo = unserialize($msg->body);
        $foo->setIsActive(true);
        $foo->setDescription('Foo object has been modified by reply');

        // Prepare reply
        $reply = new AMQPMessage(serialize($foo), [
            'correlation_id' => $msg->get('correlation_id')
        ]);
        /** @var AMQPChannel $channel */
        $channel = $msg->get('channel');
        $channel->basic_publish($reply, '', $msg->get('reply_to'));
    }
}

Lancement du serveur RPC

php app/console rabbitmq:rpc-server <name>

php app/console rabbitmq:rpc-server <name> -m 50

Picoti !

  • Facile à installer (y compris php-amqplib)
     

  • Bundle disponible depuis Symfony 2.0
     

  • Interface de configuration
     

  • Répartition de la charge

Picota !

+-------------+-------------+
| Duration    |  36 seconds |
+-------------+-------------+
| Memory peak | 1.5 MiB     |
+-------------+-------------+
+-------------+-------------+
| Duration    |  46 seconds |
+-------------+-------------+
| Memory peak | 2.5 MiB     |
+-------------+-------------+

Extension

Librairie

Benchmark réalisé par Olivier Dolbeau 

Alternatives

Sources

Illustrations : "Le coup du lapin" - Andy Riley

RabbitMQ

By Vincent Chalamon

RabbitMQ

Installation du service RabbitMQ dans une application Symfony 2 grâce au bundle RabbitMQBundle

  • 2,338
Loading comments...

More from Vincent Chalamon