RabbitMQ y Symfony
Agenda
• Introducción a RabbitMQ
• RabbitMQ y Symfony
• Rabbit en la vida real
• Problemas
Quien Soy
• CTO en chicplace.com
• Apasionado de la Web
• 8 años en PHP
• Whovian y Aerotrastornado
¿Que es RabbitMQ?
Sistema de Colas
¿Que es RabbitMQ?
• Sistema de mensajería
• Implementa AMQP
• Robusto
• Facil de Usar
¿Que he de saber?
Mensajes
• Body
• Body es lo que nosotros
decidamos (json/serialize/
binario/txt…)
• Header:
• Info para el broker
• Info para la aplicación
Mensajes
• Body
• Body es lo que nosotros
decidamos (json/serialize/
binario/txt…)
• Header:
• Info para el broker
• Info para la aplicación
Header
Content type
Content Encoding
Routing key
Delivery Mode
Message Priority
Message publishing timestamp
Expiration period
App Id
Body
{
"id": 0,
"guid":
"2cbe2352-6d0e-430b-8faf-122eea44ebf3",
"isActive": false,
"balance": "$3,243.34",
"picture": "http://placehold.it/
32x32",
"age": 25,
"eyeColor": "blue",
"name": "Lawanda Serrano",
"gender": "female",
"company": "ELEMANTRA",
"email":
"lawandaserrano@elemantra.com",
"phone": "+1 (827) 534-2331”
}
Colas
• Mensajes esperan a ser consumidos
• FIFO
• Persistente
• Exclusiva
• Autoborrado
• Round Robin
Exchanges
• Donde se envían mensajes
• Enrutar
• Tipos:
• Direct
• Fanout
• Topic
• Autoborrado
Direct Exchange
• Una cola C se subscribe a un
exchange con la clave X
• Nuevo mensaje con la clave Y
• Si X = Y enrutar a cola C
Fanout Exchange
• El exchange enrutará el
mensaje a todas las colas
subscritas
Topic Exchange
• Un mensaje es enrulado a
una o varias colas
dependiendo de un match
con un patrón.
https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_MRG/1.1/html/Messaging_User_Guide/chap-Messaging_User_Guide-
Exchanges.html
Persistencia
• ¿Que pasa cuando palma?
• Podemos guardar en disco la estructura
• Podemos guardar en disco los mensajes
• Si guardamos los mensajes debemos guardar la
estructura
https://www.youtube.com/watch?v=G1UKDccxj00
https://www.youtube.com/watch?v=G1UKDccxj00
Saber Más
Saber Más
Saber Más
Saber Más
http://www.rabbitmq.com/
Saber Más
http://www.rabbitmq.com/
http://www.slideshare.net/old_sound
Saber Más
http://www.rabbitmq.com/
http://www.slideshare.net/old_sound
http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf
Saber Más
http://www.rabbitmq.com/
http://www.slideshare.net/old_sound
http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf
http://wedevelopers.com/2014/06/23/we-developers-034-rabbitmq/
https://www.youtube.com/watch?v=jxTngPDNqk8
https://www.youtube.com/watch?v=jxTngPDNqk8
RabbitMQ y Symfony
RabbitMQ y Symfony
RabbitMQ y Symfony
• RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
RabbitMQ y Symfony
• RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
• Basado en PhpAmqpLib
RabbitMQ y Symfony
• RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
• Basado en PhpAmqpLib
• Creación de Consumidores/Productores sencilla.
RabbitMQ y Symfony
• RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
• Basado en PhpAmqpLib
• Creación de Consumidores/Productores sencilla.
• Configuración de rabbit sencilla.
RabbitMQ y Symfony
• RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
• Basado en PhpAmqpLib
• Creación de Consumidores/Productores sencilla.
• Configuración de rabbit sencilla.
• No excesivamente estable. Nuevas funcionalidades constantes
RabbitMQ y Symfony
• RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
• Basado en PhpAmqpLib
• Creación de Consumidores/Productores sencilla.
• Configuración de rabbit sencilla.
• No excesivamente estable. Nuevas funcionalidades constantes
• PhpAmqpLib (https://github.com/videlalvaro/php-amqplib)
RabbitMQ y Symfony
• RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
• Basado en PhpAmqpLib
• Creación de Consumidores/Productores sencilla.
• Configuración de rabbit sencilla.
• No excesivamente estable. Nuevas funcionalidades constantes
• PhpAmqpLib (https://github.com/videlalvaro/php-amqplib)
• La libreria de referencia.
RabbitMQ y Symfony
• RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
• Basado en PhpAmqpLib
• Creación de Consumidores/Productores sencilla.
• Configuración de rabbit sencilla.
• No excesivamente estable. Nuevas funcionalidades constantes
• PhpAmqpLib (https://github.com/videlalvaro/php-amqplib)
• La libreria de referencia.
• No te da muchas facilidades
RabbitMQ y Symfony
composer require oldsound/rabbitmq-bundle:1.3.*
Instalación
<?php

public function registerBundles()

{

$bundles = array(

new OldSoundRabbitMqBundleOldSoundRabbitMqBundle(),

);



return $bundles;

}
old_sound_rabbit_mq:
connections:
default:
host: 'localhost'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
lazy: true
producers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
consumers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
queue_options: {name: 'make-thumbnail-queue',durable: true}
callback: solilokiam.rabbitmq.make_thumbnail_consumer
RabbitMQ y Symfony
Configuración
old_sound_rabbit_mq:
connections:
default:
host: 'localhost'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
lazy: true
producers:
make_thumbnail:
connection: default
exchange_options:
name:
type: direct
durable: true
auto_delete: false
consumers:
make_thumbnail:
connection: default
exchange_options:
name:
type: direct
durable: true
auto_delete: false
queue_options: {
callback: solilokiam.rabbitmq.make_thumbnail_consumer
RabbitMQ y Symfony
Configuración
old_sound_rabbit_mq:!
connections:!
default:!
host: 'localhost'!
port: 5672!
user: 'guest'!
password: 'guest'!
vhost: '/'!
lazy: true
old_sound_rabbit_mq:
connections:
default:
host: 'localhost'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
lazy: true
producers:
make_thumbnail:
connection: default
exchange_options:
name:
type: direct
durable: true
auto_delete: false
consumers:
make_thumbnail:
connection: default
exchange_options:
name:
type: direct
durable: true
auto_delete: false
queue_options: {
callback: solilokiam.rabbitmq.make_thumbnail_consumer
RabbitMQ y Symfony
Configuración
producers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
old_sound_rabbit_mq:
connections:
default:
host: 'localhost'
port: 5672
user: 'guest'
password: 'guest'
vhost: '/'
lazy: true
producers:
make_thumbnail:
connection: default
exchange_options:
name:
type: direct
durable: true
auto_delete: false
consumers:
make_thumbnail:
connection: default
exchange_options:
name:
type: direct
durable: true
auto_delete: false
queue_options: {
callback: solilokiam.rabbitmq.make_thumbnail_consumer
RabbitMQ y Symfony
Configuración
consumers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
queue_options: {name: 'make-thumbnail-queue',durable: true}
callback: solilokiam.rabbitmq.make_thumbnail_consumer
RabbitMQ y Symfony
protected function execute(InputInterface $input, OutputInterface $output)
{
$width = $input->getOption('width')?$input->getOption('width'):300;
$height = $input->getOption('height')?$input->getOption('height'):300;
!
$finder = new Finder();
$finder
->files()
->name('*.jpg')
->name('*.png')
->name('*.gif')
->in($input->getArgument('origin'));
!
foreach($finder as $file)
{
$messageArray = array(
'original_image_path' => $file->getRealPath(),
'destination_image_path' => $input->getArgument('destination').'/'.$file->getFileName(),
'width' => $width,
'height' => $height
);
!
$this->getContainer()->get('old_sound_rabbit_mq.make_thumbnail_producer')
->setContentType('application/json')
->setDeliveryMode(2)
->publish(json_encode($messageArray));
}
}
RabbitMQ y Symfony
protected
{
$width
$height
!
$finder
$finder
!
foreach
{
);
!
}
}
$this->getContainer()
->get('old_sound_rabbit_mq.make_thumbnail_producer')
->setContentType('application/json')
->setDeliveryMode(2)
->publish(json_encode($messageArray));
https://www.youtube.com/watch?v=zadD8X9MtUg
https://www.youtube.com/watch?v=zadD8X9MtUg
RabbitMQ y Symfony
Consumidor
class MakeThumbnailConsumer implements ConsumerInterface
{
public function execute(AMQPMessage $msg)
{
$data = json_decode($msg->body);
$imagine = new Imagine();
try
{
$imagine->open($data['original_image_path'])
->resize(new Box($data['width'],$data['height']))
->save($data['destination_image_path']);
} catch(Exception $e) {
return false;
}
return true;
}
!
}
RabbitMQ y Symfony
app/console rabbitmq:consumer make_thumbnail
app/console rabbitmq:consumer make_thumbnail -m 10
app/console rabbitmq:consumer make_thumbnail -l 256
app/console rabbitmq:consumer make_thumbnail -w
app/console rabbitmq:purge --no-confirmation make_thumbnail
¿Para que sirve RabbitMQ?
• Procesos en batch.
• Desacoplar.
• Eventos asíncronos
• Logs
Problemas de la vida real
Velocidad
• Los productores producen a más velocidad de la que
los consumidores son capaces de consumir.
• Las colas se llenan
• Optimiza el consumidor• Arranca más consumidores
Problema
Solución
Diferencias de Velocidad
Problema
• Sobre la misma cola un consumidor procesa más
mensajes que el otro.
• Un consumidor acaba ocioso y el otro tiene mensajes
pendientes
Diferencias de Velocidad
http://www.cloudamqp.com/
Diferencias de Velocidad
Causa
• Algoritmo de round robin de las colas.
http://www.cloudamqp.com/
Diferencias de Velocidad
Causa
• Algoritmo de round robin de las colas.
Mensaje1
Mensaje2
Mensaje3
Mensaje4
Mensaje5
Mensaje6
Productor
http://www.cloudamqp.com/
Diferencias de Velocidad
Causa
• Algoritmo de round robin de las colas.
Mensaje1
Mensaje2
Mensaje3
Mensaje4
Mensaje5
Mensaje6
Productor
http://www.cloudamqp.com/
Diferencias de Velocidad
Causa
• Algoritmo de round robin de las colas.
Mensaje1
Mensaje2
Mensaje3
Mensaje4
Mensaje5
Mensaje6
Productor
http://www.cloudamqp.com/
Diferencias de Velocidad
Causa
• Algoritmo de round robin de las colas.
Mensaje1
Mensaje2
Mensaje3
Mensaje4
Mensaje5
Mensaje6
Productor
http://www.cloudamqp.com/
Diferencias de Velocidad
Causa
• Algoritmo de round robin de las colas.
Mensaje1
Mensaje2
Mensaje3
Mensaje4
Mensaje5
Mensaje6
Productor
Mensaje1
Mensaje3
Mensaje5
Consumidor 1
Mensaje2
Mensaje4
Mensaje6
Consumidor 2
http://www.cloudamqp.com/
Diferencias de Velocidad
Diferencias de Velocidad
Solución
• channel.basic_qos(prefetch_count=1)
Diferencias de Velocidad
Solución
• channel.basic_qos(prefetch_count=1)
consumers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
queue_options: {name: 'make-thumbnail-queue',durable: true}
callback: solilokiam.rabbitmq.make_thumbnail_consumer
qos_options: {prefetch_size=0, prefetch_count: 1, global: false}
Pierdo la conexión
Problema
• RabbitMQ cierra la conexión TCP cada x minutos.
• Mi consumidor se queda colgado
Pierdo la conexión
• Hacer que el consumer muera al cabo de un tiempo de
inactividad.
consumers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
queue_options: {name: 'make-thumbnail-queue',durable: true}
callback: solilokiam.rabbitmq.make_thumbnail_consumer
idle_timeout: 60
Pierdo la conexión
Solución
• Hacer que el consumer muera al cabo de un tiempo de
inactividad.
consumers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
queue_options: {name: 'make-thumbnail-queue',durable: true}
callback: solilokiam.rabbitmq.make_thumbnail_consumer
idle_timeout: 60
Problemas de comunicación
Problema
• El formato de mensajes producidos no es entendido
por los consumidores.
• El formato de los mensajes cambia.
https://www.youtube.com/watch?v=9ITVzXB2DWY
https://www.youtube.com/watch?v=9ITVzXB2DWY
Problemas de comunicación
Solución
• Documentación/Comunicación/Normas
• Introducir un traductor de mensajes
public function execute(AMQPMessage $msg)
{
try
{
$makeThumbnailMsg = $this->translator($msg);
$imagine = new Imagine();
$imagine->open($makeThumbnailMsg->getOriginalImagePath())
->resize(new Box($makeThumbnailMsg->getWidth(),$makeThumbnailMsg->getHeight()))
->save($makeThumbnailMsg->getDestinationImagePath());
} catch(Exception $e) {
return false;
}
!
return true;
}
Problemas de comunicación
Problemas de comunicación
class MakeThumbnailTranslator implements TranslatorInterface
{
public function translateMessage($message)
{
$data = json_decode($message->body,true);
!
if(json_last_error() !== JSON_ERROR_NONE)
{
throw new Exception('WrongMessage');
}
!
$makeThumbnailMessage = new MakeThumbnailMessage();
$makeThumbnailMessage->setOriginalImagePath($data['original_image_path']);
$makeThumbnailMessage->setDestinationImagePath($data['destination_image_path']);
$makeThumbnailMessage->setWidth($data['width']);
$makeThumbnailMessage->setHeight($data['height']);
!
return $makeThumbnailMessage;
}
!
}
La importancia del orden
Problema
• Los mensajes han de ser consumidos en el mismo
orden que se han publicado.
• Quiero tener mas de un consumer para dar velocidad.
La importancia del orden
Solución
• Sección 4.7 protocolo AMQP: […]Specifically,
contents flowing through a single path within the
server will remain ordered For contents of a given
priority flowing through a single path, we define a
content processing path as consisting of one incoming
channel, one exchange, one queue, and one outgoing
channel.
La importancia del orden
Solución
Un consumer y punto
Organización
Problema
• Múltiples tipos de mensaje.
• Múltiples significados.
• Muchos consumers vs. Un consumer
• Muchos Exchanges vs. Un exchange
Organización
Solución
• Organizar los exchanges por afinidad.
• Una cola por exchange (direct o fanout)
• Todos los mensajes al mismo consumer
• Filtramos por type
public function execute(AMQPMessage $message)
{
if (!array_key_exists($message->get('type'), $this->workers)) {
return false;
}
!
return $this->workers[$message->get('type')]->doWork($message->body);
}
Organización
Solución
• Organizar los exchanges por afinidad.
• Utilizar el routing de RabbitMQ
• Un consumer por tipo de mensaje
• Una cola por tipo de mensaje
multiple_consumers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
queues:
make_thumbnail_video:
name: make-thumbnail-video
callback: solilokiam.rabbitmq.make_thumbnail_video
routing_keys:
- video
make_thumbnail_picture:
name: make-thumbnail-picture
callback: solilokiam.rabbitmq.make_thumbnail_picture
routing_keys:
- picture
make_thumbnail_log:
name: make-thumbnail-log
callback: solilokiam.rabbitmq.make_thumbnail_log
Organización
Solución
multiple_consumers:
make_thumbnail:
connection: default
exchange_options:
name: 'make-thumbnail-exchange'
type: direct
durable: true
auto_delete: false
queues:
make_thumbnail_video:
name: make-thumbnail-video
callback: solilokiam.rabbitmq.make_thumbnail_video
routing_keys:
- video
make_thumbnail_picture:
name: make-thumbnail-picture
callback: solilokiam.rabbitmq.make_thumbnail_picture
routing_keys:
- picture
make_thumbnail_log:
name: make-thumbnail-log
callback: solilokiam.rabbitmq.make_thumbnail_log
Organización
Solución
composer require oldsound/rabbitmq-bundle:dev-master
app/console rabbitmq:multiple-consumer make_thumbnail
¿Donde esta la respuesta?
Problema
• Necesito una respuesta a los mensajes que lanzo.
• Quiero poder paralizar las llamadas.
• Lo quiero hacer con RabbitMQ.
¿Donde esta la respuesta?
Solución
rpc_clients:
string_reverser:
connection: default
rpc_servers:
string_reverse:
connection: default
callback: solilokiam.rabbitmq.string_reverser
qos_options: {prefetch_size: 0, prefetch_count: 1, global: false}
class StringReverser implements ConsumerInterface
{
protected $translator;
!
function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
!
public function execute(AMQPMessage $msg)
{
$stringReverseMessage = $this->translator->translateMessage($msg);
!
return strrev($stringReverseMessage->getString());
}
!
}
¿Donde esta la respuesta?
class StringReverser implements ConsumerInterface
{
protected $translator;
!
function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
!
public function execute(AMQPMessage $msg)
{
$stringReverseMessage = $this->translator->translateMessage($msg);
!
return strrev($stringReverseMessage->getString());
}
!
}
¿Donde esta la respuesta?
app/console rabbit:rpc-server string_reverse
¿Donde esta la respuesta?
protected function execute(InputInterface $input, OutputInterface $output)
{
$reversableString = $input->getArgument('reversable_string');
!
$message_array = array(
'string' => $reversableString
);
!
$requestIdentifier = microtime();
!
$client = $this->getContainer()->get('old_sound_rabbit_mq.string_reverser_rpc');
$client->addRequest(json_encode($message_array),'string_reverse',$requestIdentifier);
!
$replies=$client->getReplies();
!
$output->writeln($replies);
}
¿Donde esta la respuesta?
protected function execute(InputInterface $input, OutputInterface $output)
{
$reversableString = $input->getArgument('reversable_string');
!
$message_array = array(
'string' => $reversableString
);
!
$client = $this->getContainer()->get('old_sound_rabbit_mq.string_reverser_rpc');
!
$requestIdentifier = microtime();
!
$client->addRequest(json_encode($message_array),'string_reverse',$requestIdentifier);
!
$requestIdentifier2 = microtime();
!
$client->addRequest(json_encode($message_array),'string_capitalize',$requestIdentifier2);
!
$replies=$client->getReplies();
!
$output->writeln($replies[$requestIdentifier]);
$output->writeln($replies[$requestIdentifier2]);
}
Vamos a viajar
Vamos a viajar
Problema
• Nuestra aplicación esta repartida por varias Regiones
• Queremos buena latencia.
• Queremos que los mensajes se propaguen.
Vamos a viajar
Solución
• Federation
https://www.rabbitmq.com/federation.html
Vamos a viajar
https://www.rabbitmq.com/federation.html
Vamos a viajar
Vamos a viajar
Solución
• Shovel
• Se conecta a una cola en el broker de origen
• Consume el mensaje
• Publica el mensaje al broker de destino
[{rabbitmq_shovel,
[{shovels,
[{MyShovels1,
[{sources, [{broker,"amqp://guest:guest@Machine01/MyVHost"}]},
{destinations, [{broker, "amqp://guest:guest@Machine02/MyVHost"}]},
{queue, <<"MySourceQueue1">>},
{ack_mode, on_confirm},
{publish_properties, [{delivery_mode, 2}]},
{publish_fields, [{exchange, <<"MyDestExchange">>},
{routing_key, <<>>}]},
{reconnect_delay, 5}
]}
]
}]
}]
Vamos a viajar
Todavía quiero saber más
Todavía quiero saber más
Todavía quiero saber más
¿Preguntas?
https://www.youtube.com/watch?v=YZ85_Bg-1QU
¿Preguntas?
https://www.youtube.com/watch?v=YZ85_Bg-1QU
Muchas Gracias
@solilokiam
miquel@solilokiam.com

RabbitMQ y Symfony

  • 1.
  • 2.
    Agenda • Introducción aRabbitMQ • RabbitMQ y Symfony • Rabbit en la vida real • Problemas
  • 3.
    Quien Soy • CTOen chicplace.com • Apasionado de la Web • 8 años en PHP • Whovian y Aerotrastornado
  • 4.
  • 5.
  • 7.
    ¿Que es RabbitMQ? •Sistema de mensajería • Implementa AMQP • Robusto • Facil de Usar
  • 8.
  • 9.
    Mensajes • Body • Bodyes lo que nosotros decidamos (json/serialize/ binario/txt…) • Header: • Info para el broker • Info para la aplicación
  • 10.
    Mensajes • Body • Bodyes lo que nosotros decidamos (json/serialize/ binario/txt…) • Header: • Info para el broker • Info para la aplicación Header Content type Content Encoding Routing key Delivery Mode Message Priority Message publishing timestamp Expiration period App Id Body { "id": 0, "guid": "2cbe2352-6d0e-430b-8faf-122eea44ebf3", "isActive": false, "balance": "$3,243.34", "picture": "http://placehold.it/ 32x32", "age": 25, "eyeColor": "blue", "name": "Lawanda Serrano", "gender": "female", "company": "ELEMANTRA", "email": "lawandaserrano@elemantra.com", "phone": "+1 (827) 534-2331” }
  • 11.
    Colas • Mensajes esperana ser consumidos • FIFO • Persistente • Exclusiva • Autoborrado • Round Robin
  • 12.
    Exchanges • Donde seenvían mensajes • Enrutar • Tipos: • Direct • Fanout • Topic • Autoborrado
  • 13.
    Direct Exchange • Unacola C se subscribe a un exchange con la clave X • Nuevo mensaje con la clave Y • Si X = Y enrutar a cola C
  • 14.
    Fanout Exchange • Elexchange enrutará el mensaje a todas las colas subscritas
  • 15.
    Topic Exchange • Unmensaje es enrulado a una o varias colas dependiendo de un match con un patrón. https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_MRG/1.1/html/Messaging_User_Guide/chap-Messaging_User_Guide- Exchanges.html
  • 16.
    Persistencia • ¿Que pasacuando palma? • Podemos guardar en disco la estructura • Podemos guardar en disco los mensajes • Si guardamos los mensajes debemos guardar la estructura
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
    RabbitMQ y Symfony •RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
  • 31.
    RabbitMQ y Symfony •RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib
  • 32.
    RabbitMQ y Symfony •RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib • Creación de Consumidores/Productores sencilla.
  • 33.
    RabbitMQ y Symfony •RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib • Creación de Consumidores/Productores sencilla. • Configuración de rabbit sencilla.
  • 34.
    RabbitMQ y Symfony •RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib • Creación de Consumidores/Productores sencilla. • Configuración de rabbit sencilla. • No excesivamente estable. Nuevas funcionalidades constantes
  • 35.
    RabbitMQ y Symfony •RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib • Creación de Consumidores/Productores sencilla. • Configuración de rabbit sencilla. • No excesivamente estable. Nuevas funcionalidades constantes • PhpAmqpLib (https://github.com/videlalvaro/php-amqplib)
  • 36.
    RabbitMQ y Symfony •RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib • Creación de Consumidores/Productores sencilla. • Configuración de rabbit sencilla. • No excesivamente estable. Nuevas funcionalidades constantes • PhpAmqpLib (https://github.com/videlalvaro/php-amqplib) • La libreria de referencia.
  • 37.
    RabbitMQ y Symfony •RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib • Creación de Consumidores/Productores sencilla. • Configuración de rabbit sencilla. • No excesivamente estable. Nuevas funcionalidades constantes • PhpAmqpLib (https://github.com/videlalvaro/php-amqplib) • La libreria de referencia. • No te da muchas facilidades
  • 38.
    RabbitMQ y Symfony composerrequire oldsound/rabbitmq-bundle:1.3.* Instalación <?php
 public function registerBundles()
 {
 $bundles = array(
 new OldSoundRabbitMqBundleOldSoundRabbitMqBundle(),
 );
 
 return $bundles;
 }
  • 39.
    old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user:'guest' password: 'guest' vhost: '/' lazy: true producers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type: direct durable: true auto_delete: false consumers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type: direct durable: true auto_delete: false queue_options: {name: 'make-thumbnail-queue',durable: true} callback: solilokiam.rabbitmq.make_thumbnail_consumer RabbitMQ y Symfony Configuración
  • 40.
    old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user:'guest' password: 'guest' vhost: '/' lazy: true producers: make_thumbnail: connection: default exchange_options: name: type: direct durable: true auto_delete: false consumers: make_thumbnail: connection: default exchange_options: name: type: direct durable: true auto_delete: false queue_options: { callback: solilokiam.rabbitmq.make_thumbnail_consumer RabbitMQ y Symfony Configuración old_sound_rabbit_mq:! connections:! default:! host: 'localhost'! port: 5672! user: 'guest'! password: 'guest'! vhost: '/'! lazy: true
  • 41.
    old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user:'guest' password: 'guest' vhost: '/' lazy: true producers: make_thumbnail: connection: default exchange_options: name: type: direct durable: true auto_delete: false consumers: make_thumbnail: connection: default exchange_options: name: type: direct durable: true auto_delete: false queue_options: { callback: solilokiam.rabbitmq.make_thumbnail_consumer RabbitMQ y Symfony Configuración producers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type: direct durable: true auto_delete: false
  • 42.
    old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user:'guest' password: 'guest' vhost: '/' lazy: true producers: make_thumbnail: connection: default exchange_options: name: type: direct durable: true auto_delete: false consumers: make_thumbnail: connection: default exchange_options: name: type: direct durable: true auto_delete: false queue_options: { callback: solilokiam.rabbitmq.make_thumbnail_consumer RabbitMQ y Symfony Configuración consumers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type: direct durable: true auto_delete: false queue_options: {name: 'make-thumbnail-queue',durable: true} callback: solilokiam.rabbitmq.make_thumbnail_consumer
  • 43.
    RabbitMQ y Symfony protectedfunction execute(InputInterface $input, OutputInterface $output) { $width = $input->getOption('width')?$input->getOption('width'):300; $height = $input->getOption('height')?$input->getOption('height'):300; ! $finder = new Finder(); $finder ->files() ->name('*.jpg') ->name('*.png') ->name('*.gif') ->in($input->getArgument('origin')); ! foreach($finder as $file) { $messageArray = array( 'original_image_path' => $file->getRealPath(), 'destination_image_path' => $input->getArgument('destination').'/'.$file->getFileName(), 'width' => $width, 'height' => $height ); ! $this->getContainer()->get('old_sound_rabbit_mq.make_thumbnail_producer') ->setContentType('application/json') ->setDeliveryMode(2) ->publish(json_encode($messageArray)); } }
  • 44.
  • 45.
  • 46.
  • 47.
    RabbitMQ y Symfony Consumidor classMakeThumbnailConsumer implements ConsumerInterface { public function execute(AMQPMessage $msg) { $data = json_decode($msg->body); $imagine = new Imagine(); try { $imagine->open($data['original_image_path']) ->resize(new Box($data['width'],$data['height'])) ->save($data['destination_image_path']); } catch(Exception $e) { return false; } return true; } ! }
  • 48.
    RabbitMQ y Symfony app/consolerabbitmq:consumer make_thumbnail app/console rabbitmq:consumer make_thumbnail -m 10 app/console rabbitmq:consumer make_thumbnail -l 256 app/console rabbitmq:consumer make_thumbnail -w app/console rabbitmq:purge --no-confirmation make_thumbnail
  • 49.
    ¿Para que sirveRabbitMQ? • Procesos en batch. • Desacoplar. • Eventos asíncronos • Logs
  • 50.
    Problemas de lavida real
  • 51.
    Velocidad • Los productoresproducen a más velocidad de la que los consumidores son capaces de consumir. • Las colas se llenan • Optimiza el consumidor• Arranca más consumidores Problema Solución
  • 52.
    Diferencias de Velocidad Problema •Sobre la misma cola un consumidor procesa más mensajes que el otro. • Un consumidor acaba ocioso y el otro tiene mensajes pendientes
  • 53.
  • 54.
    Diferencias de Velocidad Causa •Algoritmo de round robin de las colas. http://www.cloudamqp.com/
  • 55.
    Diferencias de Velocidad Causa •Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor http://www.cloudamqp.com/
  • 56.
    Diferencias de Velocidad Causa •Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor http://www.cloudamqp.com/
  • 57.
    Diferencias de Velocidad Causa •Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor http://www.cloudamqp.com/
  • 58.
    Diferencias de Velocidad Causa •Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor http://www.cloudamqp.com/
  • 59.
    Diferencias de Velocidad Causa •Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor Mensaje1 Mensaje3 Mensaje5 Consumidor 1 Mensaje2 Mensaje4 Mensaje6 Consumidor 2 http://www.cloudamqp.com/
  • 60.
  • 61.
    Diferencias de Velocidad Solución •channel.basic_qos(prefetch_count=1)
  • 62.
    Diferencias de Velocidad Solución •channel.basic_qos(prefetch_count=1) consumers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type: direct durable: true auto_delete: false queue_options: {name: 'make-thumbnail-queue',durable: true} callback: solilokiam.rabbitmq.make_thumbnail_consumer qos_options: {prefetch_size=0, prefetch_count: 1, global: false}
  • 63.
    Pierdo la conexión Problema •RabbitMQ cierra la conexión TCP cada x minutos. • Mi consumidor se queda colgado
  • 64.
    Pierdo la conexión •Hacer que el consumer muera al cabo de un tiempo de inactividad. consumers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type: direct durable: true auto_delete: false queue_options: {name: 'make-thumbnail-queue',durable: true} callback: solilokiam.rabbitmq.make_thumbnail_consumer idle_timeout: 60
  • 65.
    Pierdo la conexión Solución •Hacer que el consumer muera al cabo de un tiempo de inactividad. consumers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type: direct durable: true auto_delete: false queue_options: {name: 'make-thumbnail-queue',durable: true} callback: solilokiam.rabbitmq.make_thumbnail_consumer idle_timeout: 60
  • 66.
    Problemas de comunicación Problema •El formato de mensajes producidos no es entendido por los consumidores. • El formato de los mensajes cambia.
  • 67.
  • 68.
  • 69.
    Problemas de comunicación Solución •Documentación/Comunicación/Normas • Introducir un traductor de mensajes
  • 70.
    public function execute(AMQPMessage$msg) { try { $makeThumbnailMsg = $this->translator($msg); $imagine = new Imagine(); $imagine->open($makeThumbnailMsg->getOriginalImagePath()) ->resize(new Box($makeThumbnailMsg->getWidth(),$makeThumbnailMsg->getHeight())) ->save($makeThumbnailMsg->getDestinationImagePath()); } catch(Exception $e) { return false; } ! return true; } Problemas de comunicación
  • 71.
    Problemas de comunicación classMakeThumbnailTranslator implements TranslatorInterface { public function translateMessage($message) { $data = json_decode($message->body,true); ! if(json_last_error() !== JSON_ERROR_NONE) { throw new Exception('WrongMessage'); } ! $makeThumbnailMessage = new MakeThumbnailMessage(); $makeThumbnailMessage->setOriginalImagePath($data['original_image_path']); $makeThumbnailMessage->setDestinationImagePath($data['destination_image_path']); $makeThumbnailMessage->setWidth($data['width']); $makeThumbnailMessage->setHeight($data['height']); ! return $makeThumbnailMessage; } ! }
  • 72.
    La importancia delorden Problema • Los mensajes han de ser consumidos en el mismo orden que se han publicado. • Quiero tener mas de un consumer para dar velocidad.
  • 73.
    La importancia delorden Solución • Sección 4.7 protocolo AMQP: […]Specifically, contents flowing through a single path within the server will remain ordered For contents of a given priority flowing through a single path, we define a content processing path as consisting of one incoming channel, one exchange, one queue, and one outgoing channel.
  • 74.
    La importancia delorden Solución Un consumer y punto
  • 75.
    Organización Problema • Múltiples tiposde mensaje. • Múltiples significados. • Muchos consumers vs. Un consumer • Muchos Exchanges vs. Un exchange
  • 76.
    Organización Solución • Organizar losexchanges por afinidad. • Una cola por exchange (direct o fanout) • Todos los mensajes al mismo consumer • Filtramos por type public function execute(AMQPMessage $message) { if (!array_key_exists($message->get('type'), $this->workers)) { return false; } ! return $this->workers[$message->get('type')]->doWork($message->body); }
  • 77.
    Organización Solución • Organizar losexchanges por afinidad. • Utilizar el routing de RabbitMQ • Un consumer por tipo de mensaje • Una cola por tipo de mensaje
  • 78.
    multiple_consumers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type:direct durable: true auto_delete: false queues: make_thumbnail_video: name: make-thumbnail-video callback: solilokiam.rabbitmq.make_thumbnail_video routing_keys: - video make_thumbnail_picture: name: make-thumbnail-picture callback: solilokiam.rabbitmq.make_thumbnail_picture routing_keys: - picture make_thumbnail_log: name: make-thumbnail-log callback: solilokiam.rabbitmq.make_thumbnail_log Organización Solución
  • 79.
    multiple_consumers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type:direct durable: true auto_delete: false queues: make_thumbnail_video: name: make-thumbnail-video callback: solilokiam.rabbitmq.make_thumbnail_video routing_keys: - video make_thumbnail_picture: name: make-thumbnail-picture callback: solilokiam.rabbitmq.make_thumbnail_picture routing_keys: - picture make_thumbnail_log: name: make-thumbnail-log callback: solilokiam.rabbitmq.make_thumbnail_log Organización Solución composer require oldsound/rabbitmq-bundle:dev-master app/console rabbitmq:multiple-consumer make_thumbnail
  • 80.
    ¿Donde esta larespuesta? Problema • Necesito una respuesta a los mensajes que lanzo. • Quiero poder paralizar las llamadas. • Lo quiero hacer con RabbitMQ.
  • 81.
    ¿Donde esta larespuesta? Solución rpc_clients: string_reverser: connection: default rpc_servers: string_reverse: connection: default callback: solilokiam.rabbitmq.string_reverser qos_options: {prefetch_size: 0, prefetch_count: 1, global: false}
  • 82.
    class StringReverser implementsConsumerInterface { protected $translator; ! function __construct(TranslatorInterface $translator) { $this->translator = $translator; } ! public function execute(AMQPMessage $msg) { $stringReverseMessage = $this->translator->translateMessage($msg); ! return strrev($stringReverseMessage->getString()); } ! } ¿Donde esta la respuesta?
  • 83.
    class StringReverser implementsConsumerInterface { protected $translator; ! function __construct(TranslatorInterface $translator) { $this->translator = $translator; } ! public function execute(AMQPMessage $msg) { $stringReverseMessage = $this->translator->translateMessage($msg); ! return strrev($stringReverseMessage->getString()); } ! } ¿Donde esta la respuesta? app/console rabbit:rpc-server string_reverse
  • 84.
    ¿Donde esta larespuesta? protected function execute(InputInterface $input, OutputInterface $output) { $reversableString = $input->getArgument('reversable_string'); ! $message_array = array( 'string' => $reversableString ); ! $requestIdentifier = microtime(); ! $client = $this->getContainer()->get('old_sound_rabbit_mq.string_reverser_rpc'); $client->addRequest(json_encode($message_array),'string_reverse',$requestIdentifier); ! $replies=$client->getReplies(); ! $output->writeln($replies); }
  • 85.
    ¿Donde esta larespuesta? protected function execute(InputInterface $input, OutputInterface $output) { $reversableString = $input->getArgument('reversable_string'); ! $message_array = array( 'string' => $reversableString ); ! $client = $this->getContainer()->get('old_sound_rabbit_mq.string_reverser_rpc'); ! $requestIdentifier = microtime(); ! $client->addRequest(json_encode($message_array),'string_reverse',$requestIdentifier); ! $requestIdentifier2 = microtime(); ! $client->addRequest(json_encode($message_array),'string_capitalize',$requestIdentifier2); ! $replies=$client->getReplies(); ! $output->writeln($replies[$requestIdentifier]); $output->writeln($replies[$requestIdentifier2]); }
  • 86.
  • 87.
    Vamos a viajar Problema •Nuestra aplicación esta repartida por varias Regiones • Queremos buena latencia. • Queremos que los mensajes se propaguen.
  • 88.
    Vamos a viajar Solución •Federation https://www.rabbitmq.com/federation.html
  • 89.
  • 90.
  • 91.
    Vamos a viajar Solución •Shovel • Se conecta a una cola en el broker de origen • Consume el mensaje • Publica el mensaje al broker de destino
  • 92.
    [{rabbitmq_shovel, [{shovels, [{MyShovels1, [{sources, [{broker,"amqp://guest:guest@Machine01/MyVHost"}]}, {destinations, [{broker,"amqp://guest:guest@Machine02/MyVHost"}]}, {queue, <<"MySourceQueue1">>}, {ack_mode, on_confirm}, {publish_properties, [{delivery_mode, 2}]}, {publish_fields, [{exchange, <<"MyDestExchange">>}, {routing_key, <<>>}]}, {reconnect_delay, 5} ]} ] }] }] Vamos a viajar
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.