angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Del código monolítico a los servicios con RabbitMQ y Pika

Utilice los ejemplos de la documentación Pika para crear sus propias clases publicadoras y consumidoras RabbitMQ .

8 enero 2023 Actualizado 9 enero 2023
post main image
https://www.pexels.com/nl-nl/@jeshoots-com-147458

Este post es sobre el uso de RabbitMQ en su aplicación Python . Si ya estás usando Rabbit, probablemente no encontrarás nada útil en este post. ¿Por qué un post sobre RabbitMQ? Porque tengo una aplicación que lo está utilizando desde hace un año, y pensé en compartir mis experiencias.

En este post, transformamos una aplicación monolítica en servicios desacoplados por colas. Además, hablaré de servicios en lugar de microservicios, tú decides lo 'grande' que es tu servicio. No hay ejemplos de código en este post.

Ejemplo de aplicación monolítica: Formulario de contacto

El ejemplo que utilizaremos es el proceso de formulario de contacto. Un user de nuestra web introduce los datos del formulario de contacto. Esto se procesa, se prepara un correo electrónico y el correo electrónico se envía a un SMTP-servidor externo.

        +-----------------+    smtp
user    | - process form  |  server
------->| - prepare email |------->
        | - send email    |
        +-----------------+

¿Por qué querrías dividir tu código en servicios?

Hay una serie de pros y contras de dividir tu código:

Pros (idealmente):

  • Separación más estricta entre los principales servicios (funciones) de tu código.
  • Los servicios pueden detenerse y reiniciarse sin pérdida de datos.
  • Rendimiento. Al desacoplar, un servicio está disponible antes.
  • Asíncrono. Un servicio esperará a que otro esté disponible, sin afectar a otros servicios.

Contras:

  • Mayor complejidad.
  • Mayor tiempo de desarrollo.
  • Mayor dificultad para realizar pruebas.

Resumen de los pros: un sistema más rápido y flexible. Resumen de los contras: (mucho) más trabajo.

Hay muchos artículos en Internet que mencionan el "infierno de los microservicios", pero hay otros tantos artículos que mencionan el "infierno monolítico". Te sugiero que investigues un poco por tu cuenta. Aquí tienes un buen artículo: 'You Don't Need Microservices', ver enlaces más abajo. Lee también los comentarios.

Los servicios después de la división

Aquí están los servicios, después de dividir nuestro código.

        +---------+     +---------+     +---------+    smtp
user    | process |     | prepare |     | send    |  server
------->| form    |---->| email   |---->| email   |-------> 
        |         |     |         |     |         |
        +---------+     +---------+     +---------+

No muy especial, esto es sólo un monolito bien estructurado.

RabbitMQ y Pika

Para desacoplar los servicios, utilizamos colas. RabbitMQ es un broker de mensajes, y soporta colas de mensajes, múltiples protocolos de mensajería, y más. Para comunicarnos con RabbitMQ en Python utilizamos el paquete Python Pika. Pika es una implementación pura-Python del protocolo AMQP 0-9-1 .

Las colas en RabbitMQ son FIFO, primero en entrar, primero en salir. Con RabbitMQ, puede definir intercambios y colas. Normalmente, se define un intercambio y, a continuación, se vinculan las colas a este intercambio.

Se utiliza un publicador para poner elementos en la cola. Un consumidor se utiliza para obtener elementos de la cola. Encontrará ejemplos de editores y consumidores en la documentación Pika .

        +-----------+     +-------------+     +-----------+    
        |           |     | RabbitMQ    |     |           |  
------->| publisher |---->| - exchange  |---->| consumer  |-------> 
        |           |     | - queue     |     |           |
        +-----------+     +-------------+     +-----------+

Los publicadores y consumidores pueden ser síncronos o asíncronos.

En muchos casos tendremos un:

  • Publicador síncrono. Esto significa que enviamos un elemento a la cola RabbitMQ y esperamos la confirmación de que el elemento ha sido colocado en la cola, antes de continuar haciendo otras cosas.
  • Consumidor asíncrono. Esto significa que nuestra función de recepción es llamada por RabbitMQ tan pronto como hay un elemento disponible en la cola.

RabbitMQ y acuses de recibo

RabbitMQ tiene muchas opciones, la más importante de entender son los acuses de recibo. Hay dos tipos de acuse de recibo.

  • Acuse de recibo positivo
  • Acuse de recibo negativo

(Sincrónico) Acuse de recibo del editor

Cuando el editor (nuestro código) envía un elemento a RabbitMQ, RabbitMQ responde enviando un:

  • Acuse de recibo positivo
    El elemento ha sido recibido por RabbitMQ
  • Acuse de recibo negativo
    Algo ha ido mal. Tal vez el intercambio o la cola no existían, la clave de enrutamiento era incorrecta.

Reconocimientos del consumidor (asíncrono)

Nuestro consumidor es llamado por RabbitMQ con el siguiente elemento de la cola. Después de procesar el elemento, el consumidor (nuestro código) responde enviando un:

  • Acuse de recibo positivo
    El elemento ha sido recibido y procesado, estoy listo para el siguiente elemento.
  • Acuse de recibo negativo
    El elemento se ha recibido pero algo ha ido mal durante el procesamiento. RabbitMQ NO eliminará el elemento de la cola y volverá a intentarlo.

Desacoplar los servicios con colas

Para desacoplar nuestros servicios, colocamos una cola entre el servicio 'process form' y el servicio 'prepare email' :

         publisher                     consumer
        +---------+     +-------+     +---------+    
user    | process |     |       |     | prepare |  
------->| form    |---->| queue |---->| email   |---->
        |         |     |       |     |         |
        +---------+     +-------+     +---------+

El servicio 'process form' actúa como editor. Contiene código para enviar elementos a la cola. El servicio 'prepare email' actúa como consumidor. Contiene código para recibir elementos en la cola.

La cola de salida es "parte" del editor.

Ahora supongamos que los dos servicios se están ejecutando en dos servidores. Como queremos que nuestros servicios se detengan y se reinicien sin pérdida de datos, es importante entender dónde ponemos la cola entre los servicios. En el servidor de publicación o en el servidor de consumo.

La respuesta es, por supuesto, que la cola debe estar en el mismo servidor que el servicio 'process form' . Así, cuando el servidor del servicio 'prepare email' esté caído, el servicio 'process form' podrá seguir funcionando. User puede seguir enviando formularios de contacto, los datos se almacenan temporalmente en la cola. Una vez que el servicio 'prepare email' vuelve a funcionar, procesa todos los datos que esperan en la cola.

                  server 1                      server 2
         - - - - - - - - - - -  - -        - - - - - - - - - - 
                                    |     | 
         publisher                             consumer
        +---------+     +-------+   |     |   +---------+    
user    | process |     |       |             | prepare |  
------->| form    |---->| queue |------------>| email   |---->
        |         |     |       |             |         |
        +---------+     +-------+   |     |   +---------+

                                    |     | 
         - - - - - - - - - - -  - -        - - - - - - - - - - 

En el ejemplo anterior, hemos utilizado servidores. Pero lo mismo se aplica cuando tienes todo en un único servidor, pero tienes dos proyectos Docker-Compose diferentes, uno para el servicio 'process form' y otro para el servicio 'prepare email' . En este caso, añadimos RabbitMQ al proyecto 'process form' Docker-Compose.

Consumidor y editor al mismo tiempo

Tiene mucho sentido que un servicio sea consumidor y editor al mismo tiempo. En nuestro ejemplo, tanto el servicio 'prepare email' como el servicio "enviar correo electrónico" actúan como consumidor y editor.

                                       consumer                      consumer
                                          +                             +
         publisher                     publisher                     publisher
        +---------+     +-------+     +---------+     +-------+     +---------+    
 user   | process |     |       |     | prepare |     |       |     | send    |  
------->| form    |---->| queue |---->| email   |---->| queue |---->| email   |---->
        |         |     |       |     |         |     |       |     |         |
        +---------+     +-------+     +---------+     +-------+     +---------+

El funcionamiento normal del servicio 'prepare email' es el siguiente:

  1. Se llama al consumidor con el siguiente elemento de la cola de entrada.
  2. Se procesa el elemento.
  3. Los nuevos datos se publican en la cola de salida.
  4. Se recibe un acuse de recibo positivo de la publicación.
  5. El consumidor envía un acuse de recibo positivo a la cola de entrada.
  6. RabbitMQ elimina el elemento de la cola de entrada.
  7. RabbitMQ llama al consumidor con el siguiente elemento de la cola de entrada, una vez disponible.

Si algo va mal en el proceso de consumo, por ejemplo, la falta de publicación en la cola de salida, el consumidor puede enviar un acuse de recibo negativo a RabbitMQ. En este caso, RabbitMQ no eliminará el elemento de la cola de entrada, sino que volverá a intentarlo.

Introducción a RabbitMQ

Empezar con RabbitMQ es abrumador. Los desarrolladores de aplicaciones quieren utilizar recetas, no leer un sinfín de documentos. Empecé instalando Docker RabbitMQ con el Plugin de Gestión. El Plugin de Gestión permite crear intercambios y colas, ver el número de elementos en las colas y mucho más.

Luego probé los ejemplos de la documentación Pika . Una vez que los entiendas, podés usar este código para construir tus propias clases publisher y consumer, para usar en tu aplicación.

Notas aleatorias

La publicación asíncrona es compleja, no la he utilizado hasta ahora. La publicación síncrona con RabbitMQ no es muy rápida, especialmente cuando quieres que los datos persistan. Y probablemente quieras que los datos en la cola persistan, porque con esta opción, los datos no se pierden cuando paras RabbitMQ o en un fallo del sistema.

Puede acelerar la publicación síncrona enviando una lista de elementos a la cola en lugar de un único elemento. Pero esto depende de su aplicación.

Yo no utilizo una única instancia RabbitMQ tolerante a fallos, sino que hago girar varias instancias RabbitMQ junto con los servicios. Estoy utilizando RabbitMQ con múltiples proyectos Docker-Compose, es sólo uno de los servicios.

RabbitMQ consume mucha memoria. Mi aplicación no es muy compleja o exigente, pero una instancia de RabbitMQ utiliza unos 200MB según Docker Stats.

Algunas personas en Internet se quejan del alto uso de CPU . He notado algunos picos de uso de CPU . RabbitMQ almacena una gran cantidad de datos, que parecen no desaparecer después de un crash/kill. Después de eliminar esto, los picos de CPU desaparecieron.

También utilizo el plugin Shovel para enviar datos de una instancia RabbitMQ a otra, utilizando Docker Network. Esto también funciona correctamente.

Puedes utilizar tu aplicación para configurar los intercambios y colas de RabbitMQ . Yo no hago esto, pero configurar los intercambios y las colas utilizando el plugin de gestión, a continuación, exportar el nuevo archivo de configuración, y copiarlo en el archivo de configuración RabbitMQ .

He estado usando Dockerized RabbitMQ ahora por más de un año y está funcionando muy fiable. Nunca he tenido que reiniciar porque algo iba mal.

Resumen

¿Vale la pena dividir tu monolítico en servicios con RabbitMQ ? Sí, pero, a menos que seas una gran empresa, no exageres porque también añade mucha complejidad. Como siempre, este dolor se hace menor una vez que empiezas a entender más de RabbitMQ. Por último, RabbitMQ debería estar en la caja de herramientas de todo desarrollador.

Enlaces / créditos

Pika
https://pika.readthedocs.io

RabbitMQ
https://www.rabbitmq.com

You Don’t Need Microservices
https://itnext.io/you-dont-need-microservices-2ad8508b9e27

Deje un comentario

Comente de forma anónima o inicie sesión para comentar.

Comentarios

Deje una respuesta.

Responda de forma anónima o inicie sesión para responder.