RabbitMQ y Symfony

1,804 views

Published on

Esta charla pretende enseñar como añadir RabbitMQ a un proyecto Symfony.

Charla realizada en Symfony Barcelona el dia 2 de julio. Podeis encontrar ejemplos de código en https://github.com/solilokiam/rabbitmqexample

RabbitMQ y Symfony

  1. 1. RabbitMQ y Symfony
  2. 2. Agenda • Introducción a RabbitMQ • RabbitMQ y Symfony • Rabbit en la vida real • Problemas
  3. 3. Quien Soy • CTO en chicplace.com • Apasionado de la Web • 8 años en PHP • Whovian y Aerotrastornado
  4. 4. ¿Que es RabbitMQ?
  5. 5. Sistema de Colas
  6. 6. ¿Que es RabbitMQ? • Sistema de mensajería • Implementa AMQP • Robusto • Facil de Usar
  7. 7. ¿Que he de saber?
  8. 8. Mensajes • Body • Body es lo que nosotros decidamos (json/serialize/ binario/txt…) • Header: • Info para el broker • Info para la aplicación
  9. 9. 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” }
  10. 10. Colas • Mensajes esperan a ser consumidos • FIFO • Persistente • Exclusiva • Autoborrado • Round Robin
  11. 11. Exchanges • Donde se envían mensajes • Enrutar • Tipos: • Direct • Fanout • Topic • Autoborrado
  12. 12. 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
  13. 13. Fanout Exchange • El exchange enrutará el mensaje a todas las colas subscritas
  14. 14. 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
  15. 15. 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
  16. 16. https://www.youtube.com/watch?v=G1UKDccxj00
  17. 17. https://www.youtube.com/watch?v=G1UKDccxj00
  18. 18. Saber Más
  19. 19. Saber Más
  20. 20. Saber Más
  21. 21. Saber Más http://www.rabbitmq.com/
  22. 22. Saber Más http://www.rabbitmq.com/ http://www.slideshare.net/old_sound
  23. 23. Saber Más http://www.rabbitmq.com/ http://www.slideshare.net/old_sound http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf
  24. 24. 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/
  25. 25. https://www.youtube.com/watch?v=jxTngPDNqk8
  26. 26. https://www.youtube.com/watch?v=jxTngPDNqk8
  27. 27. RabbitMQ y Symfony
  28. 28. RabbitMQ y Symfony
  29. 29. RabbitMQ y Symfony • RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle)
  30. 30. RabbitMQ y Symfony • RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib
  31. 31. RabbitMQ y Symfony • RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib • Creación de Consumidores/Productores sencilla.
  32. 32. RabbitMQ y Symfony • RabbitMQBundle (https://github.com/videlalvaro/rabbitmqbundle) • Basado en PhpAmqpLib • Creación de Consumidores/Productores sencilla. • Configuración de rabbit sencilla.
  33. 33. 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
  34. 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 • PhpAmqpLib (https://github.com/videlalvaro/php-amqplib)
  35. 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) • La libreria de referencia.
  36. 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. • No te da muchas facilidades
  37. 37. RabbitMQ y Symfony composer require oldsound/rabbitmq-bundle:1.3.* Instalación <?php
 public function registerBundles()
 {
 $bundles = array(
 new OldSoundRabbitMqBundleOldSoundRabbitMqBundle(),
 );
 
 return $bundles;
 }
  38. 38. 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
  39. 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: 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
  40. 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 producers: make_thumbnail: connection: default exchange_options: name: 'make-thumbnail-exchange' type: direct durable: true auto_delete: false
  41. 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 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
  42. 42. 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)); } }
  43. 43. 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));
  44. 44. https://www.youtube.com/watch?v=zadD8X9MtUg
  45. 45. https://www.youtube.com/watch?v=zadD8X9MtUg
  46. 46. 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; } ! }
  47. 47. 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
  48. 48. ¿Para que sirve RabbitMQ? • Procesos en batch. • Desacoplar. • Eventos asíncronos • Logs
  49. 49. Problemas de la vida real
  50. 50. 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
  51. 51. 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
  52. 52. Diferencias de Velocidad http://www.cloudamqp.com/
  53. 53. Diferencias de Velocidad Causa • Algoritmo de round robin de las colas. http://www.cloudamqp.com/
  54. 54. Diferencias de Velocidad Causa • Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor http://www.cloudamqp.com/
  55. 55. Diferencias de Velocidad Causa • Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor http://www.cloudamqp.com/
  56. 56. Diferencias de Velocidad Causa • Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor http://www.cloudamqp.com/
  57. 57. Diferencias de Velocidad Causa • Algoritmo de round robin de las colas. Mensaje1 Mensaje2 Mensaje3 Mensaje4 Mensaje5 Mensaje6 Productor http://www.cloudamqp.com/
  58. 58. 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/
  59. 59. Diferencias de Velocidad
  60. 60. Diferencias de Velocidad Solución • channel.basic_qos(prefetch_count=1)
  61. 61. 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}
  62. 62. Pierdo la conexión Problema • RabbitMQ cierra la conexión TCP cada x minutos. • Mi consumidor se queda colgado
  63. 63. 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
  64. 64. 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
  65. 65. Problemas de comunicación Problema • El formato de mensajes producidos no es entendido por los consumidores. • El formato de los mensajes cambia.
  66. 66. https://www.youtube.com/watch?v=9ITVzXB2DWY
  67. 67. https://www.youtube.com/watch?v=9ITVzXB2DWY
  68. 68. Problemas de comunicación Solución • Documentación/Comunicación/Normas • Introducir un traductor de mensajes
  69. 69. 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
  70. 70. 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; } ! }
  71. 71. 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.
  72. 72. 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.
  73. 73. La importancia del orden Solución Un consumer y punto
  74. 74. Organización Problema • Múltiples tipos de mensaje. • Múltiples significados. • Muchos consumers vs. Un consumer • Muchos Exchanges vs. Un exchange
  75. 75. 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); }
  76. 76. 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
  77. 77. 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
  78. 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 composer require oldsound/rabbitmq-bundle:dev-master app/console rabbitmq:multiple-consumer make_thumbnail
  79. 79. ¿Donde esta la respuesta? Problema • Necesito una respuesta a los mensajes que lanzo. • Quiero poder paralizar las llamadas. • Lo quiero hacer con RabbitMQ.
  80. 80. ¿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}
  81. 81. 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?
  82. 82. 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
  83. 83. ¿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); }
  84. 84. ¿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]); }
  85. 85. Vamos a viajar
  86. 86. Vamos a viajar Problema • Nuestra aplicación esta repartida por varias Regiones • Queremos buena latencia. • Queremos que los mensajes se propaguen.
  87. 87. Vamos a viajar Solución • Federation https://www.rabbitmq.com/federation.html
  88. 88. Vamos a viajar https://www.rabbitmq.com/federation.html
  89. 89. Vamos a viajar
  90. 90. 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
  91. 91. [{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
  92. 92. Todavía quiero saber más
  93. 93. Todavía quiero saber más
  94. 94. Todavía quiero saber más
  95. 95. ¿Preguntas? https://www.youtube.com/watch?v=YZ85_Bg-1QU
  96. 96. ¿Preguntas? https://www.youtube.com/watch?v=YZ85_Bg-1QU
  97. 97. Muchas Gracias @solilokiam miquel@solilokiam.com

×