Comunicando microservicios
A la hora de diseñar microservicios, uno de los aspectos claves es decidir que tipo de comunicación va a darse entre dichos microservicios. Como bien es sabido, la comunicación puede ser de dos tipos, de sobra conocidos:
- Síncrona, siendo una solución muy popular mediante el protocolo HTTP
- Asíncrona, por ejemplo mediante el protocolo AMQP
El propósito de este artículo no es debatir cuál de ellos es mejor, en que situaciones escoger uno u otro, ni divagar sobre otras soluciones o protocolos, sencillamente se pretende proporcionar un ejemplo sencillo en el que se refleje la comunicación de microservicios.
Supongamos el siguiente escenario (el cual es muy básico, pero perfectamente se podría dar en cualquier dominio de una aplicación), en el que tenemos una dominio en el que se pueden ver detalles de películas y descargarlas:
- Lo primero será una API mediante la cual los usuarios puedan ver detalles de las películas y descargarlas.
- Desde negocio se ha decidido que sería muy ventajoso tener un análisis en tiempo real sobre el número de descargas e impresiones que tiene cada película.
- Como negocio quiere saber como va el número de descargas e impresiones de todas nuestras pelis, también quiere un reporte donde se esclarezcan estos números. Han sugerido que lo mejor sería en un archivo PDF, y que este estuviese alojado en la nube, para poder consultarlo siempre que sea necesario, pues quieren ir contrastando el avance.
- Negocio es muy caprichoso, y no contento con tener un reporte alojado en la nube, cada vez que se genere este reporte, les encantaría recibir el mismo vía email.
Más o menos y tirando un poco de nuestra imaginación, este sería el dominio sobre el que vamos a trabajar. Una vez explicado este, se ha decido que se contendrán los siguientes microservicios:
- La API para la visualización de películas y descargas estará en un microservicio basado en Node.
- Habrá otro microservicio encargado de ir realizando el análisis de las descargas e impresiones de las películas que negocio ha solicitado, también basado en Node.
- Para el reporte, se realizará un microservicio con Spring Batch, para generar el mismo y subirlo a la nube.
- Por último, otro microservicio basado en Python, será el encargado de enviar dicho reporte por email a negocio.
El equipo de desarrollo ha decidido que para este escenario lo mejor sería que los microservicios se comunicasen asíncronamente mediante el envío de mensajes, pues su idea es orientarlo al event sourcing. Piensan además, que una buena solución para esta comunicación sería mediante el uso del protocolo AMQP y para ello, dado que el equipo quiere innovar y probar otras soluciones, tras un intensivo estudio se han decantado por RabbitMQ.
Tras la explicación de nuestro dominio, y los microservicios encargados de implementarlos, como a todos se nos dan mejor las imágenes, aquí unas cuantas:

Configurando RabbitMQ
Llegados a este punto, toca configurar RabbitMQ. RabbitMQ implementa mucho de los patrones de comunicación, tales como:
- Una cola simple
- Una cola y múltiples consumidores
- Productor/Consumidor
- Routing de mensajes
- Emisión de topics
- Patrón RPC
Tampoco es el cometido de este artículo entrar en profundidad sobre estos patrones de comunicación mediante eventos, ni discutir sus ventajas y desventajas.
De vuelta al trabajo, tras el primer contacto con RabbitMQ y tras una serie de pruebas, y estudio teórico/práctico por parte del equipo de desarrollo han decidido que para el domino con el que tratan, lo mejor sería usar los direct exchange que proporciona RabbitMQ, ya que de esta forma los mensajes serán enviados a las colas basándose en una routing key definida, de esta forma se podrán filtrar que mensajes van a qué colas y extender su funcionamiento en el futuro.
Como supondréis tendremos microservicios que actuarán como productores y otros simplemente se suscribirán a los eventos publicados (serán los workers o consumidores).
Publicando y consumiendo eventos
En primer lugar, si echáis la vista atrás, recordar que uno de nuestros microservicios será el encargado de exponer una API, que permita a los usuarios visualizar información sobre las películas y descargarlas. Cada vez que se realice una de estas acciones, se emitirá un evento al exchange correspondiente y este se enviará a las colas que tercien gracias al routing key. Antes de empezar a ver código, el dominio de los diferentes microservicios se ha diseñado bajo DDD y aunque realmente para lo sencillo que es este ejemplo me parece aumentar la complejidad con creces, puesto que no sería necesario, pero nunca está demás practicarlo :)
Ahora sí, empecemos a ver un poco de código:
Se ha creado un singleton para conectar RabbitMQ y guardar la instancia del mismo, posteriormente, tendremos varios servicios encargados de emitir un evento cuando se descarga una película, y otro cuando se visualiza. Gracias al nombre del exchange, podemos deducir que el evento se emitirá a un exchange dedicado a los eventos que se consumirán por parte de los workers encargados de realizar el análisis de estos eventos (uno de los requerimientos que impuso negocio). Ya comentado antes, una vez que un mensaje es emitido a un exchange, mediante el routing key correspondiente, se emitirá a una cola u otra. Los consumidores encargados de dichas analíticas realizarán lo siguiente:
Se conectarán a RabbitMQ, y una vez establecida la conexión, crearán el exchange y la cola (en caso de que no existiesen ya previamente). Tras la creación de la cola, ya se podrán consumir los mensajes recibidos en dicha cola. El resto de código simplemente ilustra que una vez recibido un evento, dependiendo de que tipo sea (en este caso lo podremos saber gracias a la routing key) se llamará a un caso de uso u otro (caso de uso es un concepto manejado en Clean Architecture). Al final del artículo encontraréis los enlaces a los repos, por lo que podréis ver todo el código en su completitud, por el momento centrémonos en la comunicación ^^
Una vez configurado RabbitMQ, los microservicios están listos para producir y consumir mensajes. Por último, quedaría ver como el microservicio encargado de generar el reporte (implementado en Spring Batch) emite el evento cuando dicho archivo ha sido generado y almacenado en Google Cloud Storage. Habrá otro microservicio (realizado en Python) que se encargará de consumir el evento mencionado, se descargará el reporte generado (se recibirá el nombre del reporte en el evento consumido) de Google Cloud Storage, y generará un email que llevará el reporte adjunto, y lo enviará. Esta vez se va a obviar el código, pues es igual que el visto anteriormente, solo que en esta ocasión el productor estará implementado en Java, y el consumidor en Python.
De esta forma, se ha alcanzado lo que se pretendía en un inicio, comunicar asíncronamente nuestros microservicios, de esta forma conseguiremos no bloquear nuestros procesos, y mientras los usuarios siguen pudiendo realizar visualizaciones y descargas de películas, a la vez que los microservicios consumidores irán ejecutando el análisis de las descargas e impresiones sobre las películas a medida que estas van sucediendo.
Enlace a los repositorios
Y ahora os dejo todos los enlaces a GitHub:
- Api de películas
- Analíticas de películas
- Generador de reporte (también se encarga de subir el archivo a Google Cloud Storage)
- Envío de emails con reporte
Por cierto, si estáis interesados en ejecutar todos los servicios, deberéis configurar una serie de variables de entorno (son deducibles desde cada proyecto), ya que son necesarias credenciales de Google Cloud, y diferentes correos electrónicos, así como una colección específica en MongDB (que ha sido usado como sistema de persistencia para estos ejemplos). Si de verdad queréis probarlos, poneros en contacto conmigo y os explicaré que variables tienen que ser configuradas para cada uno de los micros.