En este artículo hablamos sobre una arquitectura HATP (High Availability & Time Performance) que hemos desarrollado para escenarios con tiempos críticos.
Uno de los puntos más importantes que se han de decidir a la hora de diseñar una arquitectura está relacionado con el rendimiento y disponibilidad de los servicios. Muchas veces estos servicios dependen de escenarios más o menos exigentes respecto a la necesidad de obtener un resultado ágil (time performance) y a tener una alta disponibilidad (high availavility) para respaldar la caída de servicios.
El ejemplo que presento a continuación trata de dar apoyo a un problema de planificación de personal en un momento limitado de tiempo. En este caso, la planificación del personal puede variar en función de la previsión de la demanda, es decir, si se estiman más ventas para el día siguiente, entonces se necesitará reforzar el personal. Este interesante proyecto está separado en dos procesos diferenciados: por un lado, un trabajo de estimación de ventas mediante modelos de machine learning; y por otro, un trabajo basado en reglas de decisión de negocio que utiliza la entrada de la estimación de ventas para producir un output de mucho valor para el cliente. En este artículo me centraré únicamente en la segunda parte, ya que me parece muy elegante la solución de arquitectura que adoptamos para cumplir con los requisitos del proyecto.
Tal y como mencioné, los conceptos de time performance (TP) y high availability (HA) nos hacen pensar en soluciones que se construyen atendiendo a estos recursos:
- la utilización de clústeres en diferentes zonas de disponibilidad, que nos permite dar mayores garantías frente a la caída de un sistema, una máquina o una zona de la red.
- la carga de datos en memoria, que nos permite leer los datos desde diferentes servicios de una forma muy ágil y directa.
- la paralelización con procesos asíncronos, que nos permiten procesar el mismo trabajo de forma concurrente para agilizar el proceso completo.
Para la implementación de este proyecto se ha utilizado un patrón de arquitectura dirigida por eventos (EDA) mediante colas en RedisMQ. También aprovechamos el uso de Redis para lo que es, una base de datos clave valor muy rápida que se carga en memoria. También se utiliza un clúster de Kubernetes sobre Azure (AKS) como base para paralelizar procesos, trabajar con microservicios y obtener zonas de alta disponibilidad. Kubernetes no sólo es válido para arquitecturas de microservicios, también lo es para soluciones mixtas como ésta, mediante la ejecución de jobs de Kubernetes para la ejecución concurrente de trabajos por lotes.
Veamos un detalle de cómo funciona la arquitectura y por qué resulta interesante.
Tenemos varios servicios funcionando sobre Azure Kubernetes Service (AKS):
- Redis: Es una base de datos en memoria muy utilizada para implementar cachés. No obstante, y debido a que permite generar diferentes estructuras de datos, también puede ser utilizada para otros fines. Por ejemplo, las colas, en la que podemos hacer push de elementos por la cabeza y retirarlos por la cola a modo de FIFO. Es por tanto utilizada con doble función: caché y cola.
- Business Central: Se trata de un portal desde el que se desarrollan y despliegan los servicios de decisión. Ofrece herramientas tanto para trabajar con procesos (jbpm), como para trabajar con reglas de negocio y, a través de reglas, tablas de decisión o modelado DMN (Drools workbench).
- Fragmenter: Es un servicio que trabaja en modo K8S job y que realiza la fragmentación lógica de la información de previsión de ventas según el criterio seleccionado. El Fragmenter carga la información de previsión de ventas en la base de datos en memoria Redis. También realiza la fragmentación lógica, partiendo el trabajo en lotes y encolando cada lote en RedisMQ. Para el ejemplo quedaría una fragmentación lógica por restaurante en un instante concreto de tiempo. El Fragmenter va a utilizar las listas o conjuntos ordenados que tiene Redis como implementación para una cola. Cada fragmento lógico se encola a la espera de ser atendido por otros servicios que son los Processors.
- Processor: Son un conjunto de jobs idénticos que están escalados horizontalmente y que trabajan concurrentemente atendiendo las peticiones de la cola de Redis. Gracias a Kubernetes jobs hemos realizado un escalado del servicio hasta obtener lo que hemos denominado un ejército de pods. Cada uno de los miembros de ese ejército trabajará recogiendo solicitudes de la cola, resolviendo el problema de decisión y devolviendo el resultado a una base de datos, a la vez que notifica el estado de salida del proceso en otra cola de Redis. La gracia de esta arquitectura es que el parámetro de paralelismo lo podemos escalar según las necesidades que haya, para resolver el problema en tiempo.
- Kie Server: Alberga los servicios de decisión. Es capaz de exponer mediante api REST un servicio de decisión desplegado a través del Business Central en diferentes instancias de Kie Server. Durante el escalado de estas instancias se despliegan automáticamente los servicios de decisión en él. Esto da una potencia enorme a la arquitectura, pues el servicio de decisión desplegado en Kubernetes balancea la carga de solicitudes de los Processor hacia todos los servicios de decisión expuestos en los Kie Server. Evidentemente el servicio de decisión al que llama cada Processor está desarrollado y adaptado al tipo de fragmento para que sea lo más atómico, liviano y veloz posible.
Conclusiones
Mediante este tipo de arquitectura se obtienen escenarios en los que se pueden resolver una alta tasa de solicitudes de decisión en cortos espacios de tiempo. La utilización de una clave única y atomizada en la base de datos de Redis nos permite obtener los valores necesarios desde cada processor de una forma muy ágil e indexada, pues los datos no han de ser procesados y tienen un acceso directo con una clave única. Además, cuando introducimos datos en Redis podemos establecer un tiempo de expiración para que en las siguientes ejecuciones la memoria esté totalmente disponible.
Así mismo, la entidad job de Kubernetes permite paralelizar en pods un conjunto de processors que sean capaces de trabajar de forma concurrente consumiendo las solicitudes de la cola. Esto, junto con los servicios de decisión que se escalan y auto despliegan según las necesidades, hace que los processors tengan prácticamente una respuesta inmediata de decisión sobre los datos de entrada de previsión de ventas procedentes del modelo de machine learning.
El trabajo con procesos batch no es algo nuevo, fragmentar el trabajo en lotes para que puedan ser procesados de forma independiente ofrece una flexibilidad y se adapta realmente bien al trabajo en microservicios en Kubernetes u Openshift. De hecho, estas tecnologías no solo cuentan con workloads de tipo pod, sino también de tipo job.
Desde Business Central podemos cambiar una decisión en una regla de negocio de una forma muy ágil para que quede desplegada y disponible para los servicios de decisión de forma inmediata sin la necesidad de conocimiento técnico.
Además, si el cliente quiere reutilizar el servicio de decisión desde su red o desde Internet, el propio servicio de Kubernetes puede ser expuesto públicamente para su consumo a través de api REST por aplicaciones de terceros. Con el fin de obtener seguridad en comunicaciones, en el servicio se puede utilizar una capa de API gateway como es por ejemplo Azure API Management.
En el caso de uso de este artículo se ha pretendido dar solución al proceso de conocer la planificación de personal cada 30 minutos a lo largo de un horizonte de 14 días en cada restaurante en España atendiendo a las previsiones de venta. Para hacernos una idea de la dimensión de las llamadas a reglas, se calcula el producto cartesiano de reglas de decisión como 750 restaurantes * 2 tipos de canal de venta * predicciones en intervalos de 30 minutos durante las 14-16 horas del horario de atención = 48.000 llamadas al servicio de decisión a resolver en tiempo crítico.
Las soluciones que ofrece decide a nivel de arquitectura están siempre orientadas a satisfacer las condiciones de performance y disponibilidad que requieren nuestros clientes. Soluciones más rápidas pueden suponer un ahorro en costes para los tiempos de proceso cuando se trata de trabajar en cloud pública como Azure.
Gracias por leer el artículo. Desde el equipo de arquitectura software de decide estamos encantados de compartir esta y otra información. Podéis contactar a través de la web o redes sociales (Linkedin, Twitter, Youtube).