La Web ha cambiado mucho desde los tiempos en donde los usuarios soportaban largas esperas mientras navegaban de página a página.
En la actualidad, es inaceptable que una operación lanzada por el usuario se alargue en él tiempo o produzca un error general a causa de una degradación del servicio, o una interrupción de la conexión. Y para ello utilizamos frameworks como VueJs o similares.
Con la llegada de la computación en la nube, los problemas de fallos transitorios relacionados con las comunicaciones o la sobrecarga de los servicios de backend, ganan en criticidad y requieren de patrones de aplicaciones especialmente diseñados para Cloud.
Este artículo describe el patrón que ofrece un incremento de la disponibilidad, confiabilidad y resiliencia de nuestras aplicaciones Cloud.
Service Messaging
El patrón de mensajería es aquel que se enfrenta a los problemas que producen las conexiones permanentes entre servicios remotos como son la dependencia, el acoplamiento y la limitación de la reutilización y escalabilidad de estos.
Para ello propone realizar la comunicación por medio de un bus de mensajes que realizan una conexión asíncrona entre ambos servicios (en este caso el cliente y los servicios de facturación y almacén), tal y como se ve en la figura.
Los mensajes son unidades de información alfanumérica de pequeño tamaño (en un rango de Kilobytes) que son introducidos en la cola de mensajería por los clientes, para ser consumidos por los servicios de acuerdo con el flujo de trabajo que mejor resultados ofrezca.
Figura 2. Ejemplo de mensaje
Así, de mano, obtengo varios beneficios muy importantes:
- Disponibilidad. Puedo añadir tantos clientes como quiera, porque los servicios de bus de mensajes en Cloud son “infinitos “y con un rendimiento que escala según el tamaño de la cola.
- Cliente. El número de peticiones puede situarse muy por encima del volumen medio o el máximo esperado. El cliente no tendrá ninguna percepción de que la ejecución de la compra vaya más lenta.
- Servicios. Los servicios pueden hacer una previsión de la carga de trabajo dependiendo del número de mensajes que puedan procesar. Evitando el riesgo de tener una degradación del servicio al recibir más peticiones de las que pueda admitir; o de un escalado automático sin límites que desemboquen en un coste que desborde al previsto y aprobado.
- Escalabilidad. Podremos escalar de forma indistinta cualquiera de los servicios implicados de acuerdo con las necesidades de negocio. Por ejemplo, ante la avalancha de ventas, incrementar las instancias del servicio de facturación, dejando sin modificar el de almacén ya que la gestión del stock es mucho menos compleja. Y utilizando el tamaño de la propia cola de mensajes como indicador en las reglas automáticas de crecimiento y decrecimiento del número de instancias.
- Resiliencia. Si una vez realizada la operación de venta en la caja y enviado el mensaje, hubiese un corte o degradación de las comunicaciones, el cliente no sufriría ninguna desconexión o fallo, siguiendo trabajando en modo local hasta que volviese la conexión y se envíen los mensajes producidos. En el caso de que una instancia del servicio de facturación se cayera o tuviera algún problema, se podría continuar realizando su trabajo con cualquier otra instancia, sin perder información de estado o mensajes.
Bregando con estados
Desde el nacimiento de la web basada en http, me tengo que “pegar” con un sistema sin estado; y aún más si quiero desplegar en el Cloud en donde el crecimiento horizontal basado en instancias es el modelo de escalado por defecto.
Es cierto que puedo utilizar mecanismos más o menos eficientes como registros en base de datos o memorias caché compartidas, pero solo cuando me veo obligado a ello.
De acuerdo con esta arquitectura, sería más correcto implementar un patrón de mensajes con metadatos, incluyendo un valor de estado en el propio mensaje que se modificaría según el servicio que lo haya procesado.
Otra forma de gestionar estados sería utilizar un patrón de colas prioritarias. En donde el propio cliente decide la urgencia de la operación simplemente ingresando el mensaje en una de las colas disponibles, y que son consumidas por los servicios en un orden temporal establecido. Creándose de forma sencilla múltiples “pipelines” o flujos de procesamiento sin necesidad de persistir ningún estado.
Finalmente podríamos enviar la respuesta al cliente que dio de alta la operación de compra, añadiendo la identificación única de a quien hay que remitirle el resultado del proceso de compra en el propio mensaje.
Latencia y complejidad, los efectos secundarios
Pero todo tiene su lado oscuro, y este patrón de aplicaciones en cloud no iba a ser diferente.
Así, los inconvenientes vienen primero por el aumento de la complejidad de la arquitectura de mis aplicaciones. No solamente por tener que añadir más código para que el cliente interactúe con la cola de mensajes, si no también por construir servicios que sean idempotentes, que soporten múltiples lecturas del mismo mensaje, que sepan cómo gestionar los metadatos, o que breguen con el TTL de los mensajes.
Por otro lado, estoy metiendo latencia a mi sistema y un punto de incertidumbre; ya que la arquitectura no está diseñada a las prestaciones puras, si no a la escalabilidad, disponibilidad y resiliencia.
De forma que, dos operaciones realizadas en el mismo momento serian procesadas en un intervalo de tiempo diferente, con una duración indefinida dentro de un rango temporal aproximado.
En resumen, aún siendo un patrón que se puede aplicar en múltiples escenarios, no es una “bala de plata”. Y hay que descartarlo en aquellos en donde las operaciones en tiempo real sean requisito.
Show me the Code
Quiero compartir dos excelentes tutoriales paso a paso publicados por Microsoft, en donde muestran lo sencillo que es implantar este patrón con C#:
- Introducción al Almacenamiento en cola de Azure mediante .NET
- Introducción a Queue Storage y a los servicios conectados de Visual Studio (ASP.NET Core)
Partiendo de ambos tutoriales, lo siguiente a implantar sería el reemplazo del uso de la clave de acceso por un patrón Valet Key, para tener cubiertos mis requisitos de seguridad.
En GenbetaDev | 3 patrones de diseño imprescindibles que deberías conocer para tu sistema en cloud: Retry, Valet Key y Sharding
Imágenes: Azure Interactives
Ver todos los comentarios en https://www.genbeta.com
VER 0 Comentario