Skip to main content
Arquitecturas de microservicios: Accounting y Transaccionalidad

Arquitecturas de microservicios: Accounting y Transaccionalidad

Imagen: Bald Eagle by luis Alberto Fernández Arnanz

Cuando nos enfrentamos a la implantación de una arquitectura orientada a microservicios, debemos tener en cuenta dos de los principales problemas inherentes dentro de este tipo de arquitecturas: la gestión de log y la transaccionalidad en las operaciones.

Como ya hemos visto con anterioridad, los microservicios cumplen con el principio de responsabilidad única, lo cual implica que dentro de nuestra infraestructura dispondríamos de una cantidad bastante considerable de este tipo de aplicativos diferentes generando log. Esta problemática unida al posible tiempo de vida de los distintos microservicios y a su gestión entre los distintos entornos (desarrollo, preproducción y producción), convierte la gestión de log en algo prioritario dentro de estas arquitecturas.

Por otro lado, los servicios por definición deberían ser stateless (sin estado), y por consiguiente, los microservicios también. Esto permite que la escalabilidad horizontal de nuestros microservicios sea fácil de llevar a cabo. Sin embargo, la escalabilidad horizontal en servicios sin estado plantea otro problema mayor que la centralización de log: la transaccionalidad en las operaciones y la consistencia de los datos. ¿Quién me asegura que no existan condiciones de carrera entre los microservicios que acceden a bases de datos y me provoquen inconsistencias en los datos? Y si eso pasa, ¿Cómo gestiono la transaccionalidad de mis operaciones si mi arquitectura de microservicios está planteada stateless para que sea altamente escalable?

 

Accounting (Centralización de logs)

Por cada entorno (desarrollo, preproducción y producción) nos encontramos con una fuente de log inagotable: generado por los aplicativos, generado por los contenedores ligeros y JVMs, y del propio servidor, ya sea físico o virtualizado. Además, tenemos que recordar que nuestra arquitectura está dividida en microservicios, por lo que una misma petición puede requerir múltiples de estos servicios para ser completada.

Para comenzar a dar solución a este problema, lo primero que se debería de hacer sería definir un mensaje estándar dentro de nuestra infraestructura que contuviera los metadatos necesarios para identificarlo dentro del ámbito de una operación que pertenece a un usuario. De esta forma, aunque nuestra plataforma de microservicios sea stateless, los mensajes en vuelo dentro de ella irían identificados en todo momento por el usuario que realiza la solicitud y un identificador autogenerado de dicha operación en curso. Un ejemplo de solicitud bastante simple podría ser la que se muestra en la siguiente figura:

requestMetadata

Una vez disponemos de mensajes auto-identificados dentro de nuestra plataforma, podemos apoyarnos en Log4j2 y Logstash para generar un mensaje estándar de log en formato JSON idéntico para todos nuestros entornos. Una buena práctica sería incluir dicho mensaje de log, además de los datos identificativos del mensaje en vuelo, el entorno desde el que se genera el mensaje de log, la IP de la máquina y el tipo/nombre de microservicio.

Una vez definido nuestro mensaje de log estándar dentro de nuestra plataforma, el siguiente paso sería realizar una gestión de log centralizada de todos los entornos. Para llevar a cabo esta centralización, tendríamos que tener en cuenta que no debemos provocar un DoS  en el sistema que lo centralice, ni tampoco provocar que las redes que dan servicio se saturen de mensajes de log, evitando de este modo, que nuestra plataforma proporcione servicio útil a nuestros usuarios.

Con vistas a evitar la saturación de nuestras redes de servicio, una buena práctica sería crear distintas VLANs (virtual LAN) con el fin de disponer de redes lógicas independientes de las de servicio a través de las cuales sólo se enviarán mensajes de log. De este modo, se evitará ruido dentro de las redes de servicios, y por otro lado, se podrán identificar mucho más fácilmente patrones de mensajes anómalos en las VLANs de log, ya que todo el tráfico que fluye por dichas redes debería seguir el estándar de mensaje unificado de log definido con anterioridad.

Finalmente, y una vez hemos definido el medio por el cual viajarán los mensajes de log, faltaría solventar el problema del DoS en el sistema que centralice los log de toda nuestra infraestructura. Para ello, una de las múltiples opciones posibles sería la de apoyarnos en una gestión de mensajes asíncrona basada en colas. Es en este punto cuando Apache Kafka sería la mejor opción para centralizar todos estos mensajes en un sistema único, con vistas a ser consumido en un flujo constante por el agente (o agentes) encargado de persistir toda esta información de log en cualquier base de datos NoSQL.

Un diseño a alto nivel de la infraestructura planteada de centralización de log sería la que se muestra en la siguiente figura. En ella, se ha dibujado como consumidores unos indexer de Logstash que vuelcan los datos en un Elasticsearch y que, posteriormente, se visualizan de manera estática en Kibana. Uniendo Kibana y Shield podríamos disponer de una visualización de los distintos dashboard en función de los entornos y de los roles que tienen permiso de visualización de dichos dashboard, evitando así por ejemplo, que los log de producción sean visualizados por personas que no deberían.

AccountingMicroservices

Si quisiéramos dar un paso más allá,  podríamos jugar con los “consumer group” de Apache Kafka para que los mensajes fueran simultáneamente consumidos, tanto por los indexer de Logstash con el fin que ya hemos comentado, así como por sistemas de Fast-Data como Apache Storm con el fin de procesar en tiempo real toda esa información de log centralizada.

 

Transaccionalidad en las operaciones y consistencia de los datos

Cuando por necesidad de nuestro negocio necesitamos transaccionalidad en las operaciones dentro de nuestra arquitectura de microservicios, al ser éstos servicios stateless, se nos plantea un problema bastante serio que debemos resolver en tiempo de diseño. Dicho problema se divide a su vez en dos partes: cómo gestionar la transacionalidad de las operaciones y cómo evitar los problemas devenidos del paralelismo sobre la capa de persistencia, como por ejemplo, el problema de las lecturas sucias y las escrituras perdidas.

En primer lugar, hay que identificar claramente que microservicios estarían afectados por el arco de la transaccionalidad. Este arco nunca debería abarcar más microservicios de los imprescindiblemente necesarios excluyendo, por ejemplo, invocaciones a servicios de consulta que no modifican datos o servicios de cómputo o enrutamiento.

Una vez disponemos de la información de qué microservicios estarán involucrados en el arco de la transaccionalidad de cada operación en concreto, llega el momento de decidir cómo gestionar dicha transaccionalidad. Para ello se pueden emplear muchas técnicas y herramientas, entre ellas, las siguientes tres posibles soluciones que pueden adecuarse mejor o peor dependiendo de la problemática y necesidades de cada caso.

  • La primera opción es la más obvia y consistiría en apoyarse en cualquier motor de orquestación que gestione transaccionalidad utilizando, por ejemplo, BPEL o BPM. Esta opción plantea romper con la filosofía de los microservicios ya que dichos motores de BPEL/BPM se apoyan en esquemas en base de datos y no suelen ser tan fácilmente escalables horizontalmente como los propios microservicios. Sin embargo, al apoyarse dichos motores sobre bases de datos, son capaces de gestionar el estado de las peticiones en vuelo más allá de los reinicios del servidor, pudiendo retomar el flujo por donde se quedase, una vez vuelva a estar el servidor arriba. Esto permite gestionar las acciones de compensación o rollback de las operaciones que hayamos definido con mucha facilidad.
  • La segunda opción sería la de utilizar el patrón de ESB o los propios microservicios apoyados en una gestión de estado asíncrona basada en colas. El ESB y los microservicios, por definición son stateless, lo cual plantea la necesidad de persistir su estado de peticiones en vuelo en una capa de persistencia mucho más rápida que las bases de datos. Obviamente, se debería mantener la información de por dónde ha pasado y que métodos debería ejecutar la compensación en caso de fallo en el propio mensaje en vuelo de tal forma que éste fuera auto contenido (como se ha comentado en el punto de centralización de log).
    Aunque esta solución se adecúa más a las arquitecturas de microservicios, al ser más escalable, plantea un mayor conocimiento y entendimiento del concepto de asincronía basada en colas y de la correlación de mensajes, a la vez que un mayor esfuerzo en desarrollo, comparado con la solución planteada en el primer punto.
  • Para terminar, como tercera opción, se podría plantear un desarrollo a medida basado en cachés en memoria como pudiera ser Redis, Coherence, Infinispan, o directamente apoyarnos en bases de datos relacionales en memoria con gestión de transaccionaldidad, como por ejemplo VoltDB.
    Mientras que en las cachés en memoria habría que realizar un gran desarrollo para solventar el problema de mantener en caché los datos de las peticiones en vuelo a través de nuestra plataforma de microservicios y simular la transacionalidad, con VoltDB podemos gestionar mediante una base de datos SQL en memoria que permite procedimientos almacenados desarrollados en Java, toda la gestión de transaccionaldidad para posteriormente, volcarlo de manera desatendida en nuestra capa de persistencia real.
    El principio de esta opción sería algo así como el funcionamiento de Apache Cassandra, es decir, en memoria tenemos siempre el dato actualizado, habiendo almacenado la operación en la lista de tareas pendientes para que la capa de persistencia la lleve a cabo cuando pueda.
    Obviamente esta opción implica un mayor desarrollo que las anteriores, pero sin embargo, el rendimiento al estar todo gestionado en memoria, es mucho mayor y permite una arquitectura de microservicios mucho más escalable que las dos anteriores.

 

Conclusiones

Debido a que la automatización y la escalabilidad horizontal están encauzando a las empresas hacia los entornos IaaS, PaaS y SaaS con vistas a abaratar costes y mejorar el “time to market”, la importancia de los mensajes/peticiones comienza a premiar por encima de los propios microservicios. Es por este motivo que se hace tan importante una correcta definición de los metadatos que acompañan e identifican unívocamente al mensaje dentro de nuestra infraestructura, y por consiguiente, es casi igual de importante que dicha traza dentro de nuestro entorno Cloud esté centralizada correctamente a la hora de buscar por dónde ha pasado o qué ha hecho un mensaje dentro de nuestro sistema.

Para terminar, hay que tener en cuenta que en el momento que nuestro negocio requiera de transaccionalidad en las operaciones y consistencia en los datos, las arquitecturas de microservicios van a plantear un reto muy interesante para ávidos arquitectos que quieran definir cuál deberá ser la mejor forma de solventar los problemas de paralelismo y gestión de transaccionalidad que dichas arquitecturas presentan. Para ello, se ha expuesto una serie de ideas que seguramente podrán servir de base para llevarlo a cabo, aunque como se ha comentado en dicho punto, cada opción planteada deberá adecuarse a la casuística y problemática concreta de cada uno, por lo que recordad que cada problema es un mundo y que en la imaginación está el límite, así que usadla con moderación.

 

 

 

Elías Grande Rubio

Arquitecto SOA/BPM y Consultor de Seguridad TIC pragmático e investigador. Apasionado de la seguridad, las tecnologías middleware y de la computación de altas prestaciones. Diseñador de ideas revolucionarias que me hubieran hecho rico… pero las guardé en Megaupload y ahora escribo blogs.

Elías Grande Rubio ha escrito 7 entradas


Elías Grande Rubio

Arquitecto SOA/BPM y Consultor de Seguridad TIC pragmático e investigador. Apasionado de la seguridad, las tecnologías middleware y de la computación de altas prestaciones. Diseñador de ideas revolucionarias que me hubieran hecho rico… pero las guardé en Megaupload y ahora escribo blogs.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *