Prácticamente todas las aplicaciones modernas requieren del envío de mensajes, ya sea para aplicaciones externas o internar, donde forzosamente tendrá que pasar por la red, ya sea local o internet, lo que siempre puede tener problemas de comunicación y eventualmente la pedida de algún mensaje, lo que nos lleva a crear estrategias de comunicación robustas que aseguren la entrega de los mensajes.
Problemática
El principal problema de las aplicaciones distribuidas como SOA o Microservicios es la comunicación, ya que, al tener múltiples componentes distribuidos, estamos en riesgo de que algún mensaje no llegue a su destino. Esto puede ocurrir por varias razones, pero las principales pueden ser fallas en la red, en las aplicaciones o que estas no estén disponibles.
Si bien existen muchos motivos por los cuales un mensaje puede no ser entregado, los tres más importantes son los que se ilustran en la imagen anterior.
El primer caso (arriba) es cuando existe un problema de comunicación entre los dos componentes, en este caso se puede dar por ejemplo por una falla en la red o en el internet que impida que los dos componentes se comuniquen entre sí, lo que provocara que el Microservicio A no pueda entregar el mensaje. En este escenario al menos el Microservicios A sabrá que el mensaje no pudo ser entregado y podrá actuar en consecuencia, pero tendremos que hacer echar atrás la transacción en A, porque no logramos terminar todo el proceso, para finalmente, mostrar algún error al usuario.
En el segundo caso (en medio), la red está funcionando correctamente, pero el componente B no, lo que hace que nuevamente el mensaje no pueda ser entregado, lo que implicaría nuevamente echar a atrás la transacción en A y notificar al usuario.
Sin embargo, el tercer caso (abajo) es el más peligroso, ya que tanto la red como el componente B están operando correctamente, por lo que A puede entregar el mensaje a B, sin embargo, una vez que B recibe el mensaje, este falla por algún motivo, lo que provoca un problema grava, ya que A esta seguro que B recibió el mensaje, sin embargo B no lo pudo procesar, e incluso, como parte de la falla, este perdió el mensaje, lo que provoca un estado inconsistente entre las dos aplicaciones.
Para comprender la magnitud de este problema, imagina que el componente A es el que procesa el pedido y el B es el que se encarga del embarque (envío) del producto al cliente. El componente A habrá confirmado el pedido y realizado el cobro, pero B que es el encargado del envío del paquete, nunca lo enviará.
Solución
Una de las soluciones más utilizadas es implementar lo que se conoce como Store and Forward, que consiste en el almacenamiento local de los mensajes para su envío a un servidor remoto, de esta forma, el mensaje se almacena primeramente de forma local para impedir que se pierda, luego, el mensaje local es reenviado al servidor remoto cuando este puede aceptar el mensaje, de esta forma se garantiza la entrega del mensaje incluso si la aplicación destino no está en condiciones de recibirlo en ese momento.
Para lograr una arquitectura que implemente Store and Forward es indispensable eliminar las llamadas directas entre componentes, pues en una arquitectura distribuidas las conexiones de punta a punta son las más propensas a fallar. Entonces, eliminamos las llamadas directas y en su lugar, debemos de poner algo conocido como MOM (Midleware Orientado a Mensajes), los cuales son sistemas especializados en la entrega de mensajes. El MOM recibirá los mensajes del remitente y los persistirá, evitando que una falla o un reinicio produzca la pérdida del mensaje. Por otro lado, el receptor se registrará con el MOM para que este le envíe los mensajes cuando esté disponible.
En la imagen anterior se muestra como quedaría la arquitectura más básica para implementar un Store and Forward, el cual consiste en un Productor (el que produce el mensaje), el MOM que es el sistema especializado en la transmisión de mensajes de alta fidelidad, y el Consumidor (el destinatario de los mensajes). La secuencia de ejecución es la siguiente:
El Producer genera un nuevo mensaje que es transmitido al MOM
El MOM acepta el mensaje y lo persisten para garantizar que no se pierda ante una falla o reinicio del sistema.
El MOM envía el mensaje al consumidor
El MOM borrar el mensaje una vez que fue entregado.
Algo a tomar en cuenta es que todo este proceso es asíncrono, por lo que el producer solo deja el mensaje y se olvida de él, lo que pase de allí en adelante ya no es de su incumbencia y puede seguir trabajado con la garantía de que será entregado.
Si bien, la imagen anterior es la forma más básica de lograr el Store and Forward, existe una variante que es considerada la más robusta, pues permite persistir los mensajes en cada etapa de la transmisión de los mensajes, la cual luce de la siguiente manera:
La imagen anterior muestra como algunos productos más avanzado logran el Store and forward, el cual consiste en ir persistiendo el mensaje en todos los puntos por lo que se transmite, y en este caso en particular, podemos ver como el mensaje se transmite de MOM a MOM, para garantizar que el mensaje será entregado.
Un ejemplo rápido de algo que utiliza esta arquitectura es el correo electrónico, ya que este funciona replicando los mensajes de un servidor a otros, es decir, cuando enviamos un mail, este se almacena en el servidor SMTP de nuestro proveedor de correo y luego este lo replica al servidor SMTP del destinatario, para finalmente, ser entregado en el cliente de correo de nuestro destinatario, en este caso, nuestro Outlook sería producer, el servidor SMTP sería el MOM y el Outlook del destinatario sería el consumer.
Conclusiones
Si bien no hemos podidos implementar un Store and Forward más completo que persista los mensajes de ambos lados, sí que hemos demostrado las ventajas que ofrece este patrón, pues permite que el productor de los mensajes envíe el mensaje y se olvide de él, pero con la seguridad de que sus mensajes serán entregados tarde o temprano, lo que le da la seguridad de continuar con el proceso.
En la práctica, este patrón es altamente utilizado, no solo porque garantiza la entrega de los mensajes, si no que permite que el consumidor de los mensajes pueda ir extrayendo y procesando los mensajes a su propio ritmo, algo así como un embudo, donde los productores pueden enviar miles o millos de mensajes y el consumidor los irá tomando a medida que pueda procesarlos, de esta forma, evitamos asfixiar al consumidor con tantos mensajes.
Acerca de este libro
Todo lo que acabas de ver en este artículo es solo una pequeña parte del libro Introducción a la arquitectura de software, el libro más completo en español sobre arquitectura de software, donde cubrimos los temas más importantes para convertirte en un arquitecto de software profesional.
¿Quieres convertirte en arquitecto de software pero no sabes cuál es el camino adecuando? o simplemente no sabes que guía estudiar para convertirte en arquitecto de software, te invito a que veas mi libro: