Arquitectura de microservicios para niños
La arquitectura de microservicios (en inglés, Micro Services Architecture, MSA) es una aproximación para el desarrollo de software que consiste en construir una aplicación como un conjunto de pequeños servicios, los cuales se ejecutan en su propio proceso y se comunican con mecanismos ligeros (normalmente una API de recursos HTTP). Cada servicio se encarga de implementar una funcionalidad completa del negocio. Cada servicio es desplegado de forma independiente y puede estar programado en distintos lenguajes y usar diferentes tecnologías de almacenamiento de datos.
Se suele considerar la arquitectura de microservicios como una forma específica de realizar una arquitectura orientada a servicios.
Características
En el mundo real, no todas las implementaciones de este estilo de arquitecturas siguen las mismas características, pero la mayor parte de las arquitecturas de microservicios tienen la mayor parte de las siguientes características:
- Los componentes son servicios. La principal manera de crear componentes (unidad de software independientemente reemplazable y actualizable) es mediante la inserción de un botón que automáticamente por detrás, gestione la decomposición en servicios en lugar de bibliotecas. Los servicios son componentes separados que se comunican mediante mecanismos como los servicios web o los RPC en lugar de usar llamadas a funciones en memoria como hacen las bibliotecas.
- Organizada en torno a las funcionalidades del negocio. El sistema se divide en distintos servicios donde cada uno está organizado en torno a una capacidad del negocio. Es muy importante limitar la responsabilidad de cada servicio. Cada servicio implementa toda la funcionalidad del negocio que agrupa desde la interfaz de usuario, la persistencia en el almacenamiento y cualquiera de las colaboraciones externas.
- Productos, no proyectos. En esta arquitectura normalmente se sigue la idea de que un equipo debe estar a cargo de un componente (servicio) durante todo el ciclo de vida del mismo, desde la etapa de diseño y construcción, la fase de producción y hasta la de mantenimiento. Esta mentalidad se acopla bien con la vinculación a una capacidad del negocio. En lugar de ver el software como un conjunto de funcionalidades terminadas se ve como una relación continua, donde la pregunta es cómo puede el software ayudar a sus usuarios a mejorar la funcionalidad del negocio que implementa. Esto es facilitado por el bajo nivel de granularidad que ofrecen los microservicios.
- Extremos inteligentes, tuberías bobas. Las aplicaciones creadas desde microservicios pretenden ser tan disociadas y cohesivas como sea posible, ellas poseen su propia lógica de dominio y actúan como filtros en el clásico sentido UNIX: recibe una solicitud, aplica la lógica apropiada y produce una respuesta. Estos pasos son coreografiados usando protocolos simples (típicamente HTTP con REST o mensajería liviana como RabbitMQ o ZeroMQ) en lugar de protocolos complejos como WS-BPEL.
- Tener gobierno descentralizado permite usar tecnologías que se adapten mejor a cada funcionalidad. Con el sistema con múltiples servicios colaborativos, podemos decidir utilizar diferentes lenguajes de programación y tecnologías dentro de cada servicio. De esta forma podemos elegir la herramienta adecuada para cada tipo de trabajo en lugar de tener una estandarizada. Por ejemplo si una parte del sistema necesita mejorar su rendimiento es posible usar una tecnología, quizás más complicada, que permita alcanzar el nivel de rendimiento requerido. Otro ejemplo sería usar para ciertas cosas (reflejar interacciones entre usuarios) una base de datos orientada a grafos, y usar para otra bases de datos orientadas a documentos. la arquitectura de microservicios permite adoptar nuevas tecnologías más rápido y en aquellos lugares donde se puede aprovechar su potencial ya que se acota el impacto.
- Gestión de datos descentralizada. Los microservicios prefieren dejar a cada servicio que gestione su propia base de datos, sean estos diferentes instancias de la misma tecnología de base de datos o sistemas de base de datos completamente diferentes. Por ejemplo podríamos tener Redis para sesiones de usuarios (base de datos en memoria), MySQL (relacional) para los datos de pago, MongoDB (orientada a documentos) para el catálogo de productos, Neo4j (orientada a grafos) para las recomendaciones y Apache Cassandra (orientado a clave-valor) para el análisis de logs y analíticas. El estilo de microservicios tiene implicaciones en el manejo de las actualizaciones las cuales tradicionalmente han usado transacciones para garantizar la consistencia. Las transacciones impone un acoplamiento temporal lo que se vuelve problemático cuando hay varios servicios. Como las transacciones distribuidas son mucho más difíciles de implementar, las arquitecturas de microservicios promueven la coordinación no transaccional entre servicios, con el reconocimiento explícito que la consistencia puede ser una consistencia eventual y los problemas son compensados operativamente. El sistema merece la pena siempre y cuando el costo de solucionar los errores sea menor que el costo de perder negocios por una mayor consistencia. Los microservicios no obligan a tener distintas tecnologías de almacenamiento, solo lo permiten.
- Diseño tolerante a fallos. Las aplicaciones necesitan ser diseñadas de modo que puedan tolerar las fallas de los distintos servicios. Cualquier llamada de servicio puede fallar y el cliente tiene que ser capaz de responder a esto con la mayor facilidad y eficacia posible, evitando los muy habituales fallos en cascada de las arquitecturas distribuidas. Patrones más importantes para conseguir estabilidad que se usan en la arquitectura de microservicios:
- Usar tiempos de espera máximos. Es un mecanismo simple que permite dejar de seguir esperando por una respuesta que consideramos que ya no vendrá. Asociado al vencimiento de un tiempo de espera es frecuente que aparezcan:
- Reintento. Consiste en repetir una operación para el cual finalizó su tiempo de espera
- Encolar para reintentar la operación para ser realizada más tarde
- Disyuntores. Funcionan de forma similar a los interruptores automáticos accionados por sobrecargas que hay en las instalaciones eléctricas. En el software existen para permitir que un subsistema ante una falla no destruya el sistema entero por sobrecarga y una vez que el peligro ha pasado pueda reestablecerse. Este mecanismo se suele usar para envolver operaciones peligrosas con un componente y así poder esquivar las llamadas cuando el sistema no esté operativo. Si el disyuntor detecta que las fallas superan una frecuencia umbral el disyuntor salta abriéndose y las llamadas fallan sin realizar ningún intento de ejecutar una operación real. Después de esperar un tiempo adecuado se decide que la operación tiene una oportunidad y pasa a un estado de semiabierto en el que la próxima llamada es permitida, si tiene éxito entonces el disyuntor se vuelve a cerrar y todo vuelve a funcionar normalmente, si falla el disyuntor se vuelve a abrir y se vuelve a esperar el tiempo adecuado para intentar.
- Compartimentos estancos para contención de daños manteniendolos aislados. La forma más común de tenerlos es usando redundancia física teniendo por ejemplo varios servidores y dentro de cada servidor varias instancias. A gran escala podríamos tener varias granjas de servidores.
- Usar tiempos de espera máximos. Es un mecanismo simple que permite dejar de seguir esperando por una respuesta que consideramos que ya no vendrá. Asociado al vencimiento de un tiempo de espera es frecuente que aparezcan:
- Automatización de la infraestructura. La mayoría de los productos y sistemas desarrollados con el enfoque de microservicios han sido construidos por equipo que usan entrega continua y su precursor la integración continua. Para conseguir esto es necesario:
- Automatizar todo el proceso, desde el chequeo del código, pruebas, despliegue, ...
- Control de versiones y gestión de configuración. Todo el software tiene que estar versionado y poder gestionar las distintas configuraciones para conseguir la entrega continua.
- Arquitectura adecuada. La arquitectura tiene que permitir realizar cambios sin que afecten al resto del sistema. La arquitectura de microservicios lo hace posible.
- Diseño evolutivo. Cuando se divide el sistema en servicios hay que tener en cuenta que cada uno tiene que poder ser reemplazado o actualizado de forma independiente. Es decir, tiene que permitir una fácil evolución. El diseño del servicio tiene que ser de tal forma que evite en lo posible que la evolución de los servicios afecte a sus consumidores.
Integración de servicios
Cuando tratamos con problemas complejos, es necesario tratar con el problema de manejar procesos de negocio que involucran a varios servicios. Hay dos formas de abordar este tipo de problemas: Orquestación y coreografía.
- Con orquestación lo que hacemos es tener un software que guíe y dirija el proceso, de forma similar a como lo hace un director de orquesta.
- Con coreografía lo que hacemos es dejar que cada parte del sistema realice su trabajo y se les deja trabajar en los detalles, como hacen los bailarines en un ballet. Es más adaptado a la arquitectura de microservicios que cada servicio sepa cómo actuar en cada momento, e interactúe con otros, en lugar de tener a alguien que los coordine. Por eso para integrar se suele preferir tener coreografía.
Ejemplo
Veamos un ejemplo de un sistema en el que cuando doy de alta un cliente tengo que:
- Acreditarle una cantidad de puntos de bienvenida en su cuenta de fidelidad.
- Enviarle un kit de bienvenida a través del correo postal.
- Enviarle un correo electrónico de bienvenida al cliente.
La estrategia a tomar con un mecanismo de orquestación sería que el servicio cliente contenga la lógica de control y en la creación este se comunicaría con el Banco de puntos de fidelidad, el Servicio de correo postal y el Servicio de correo electrónico a través de una serie de llamadas petición/respuesta. Por tanto el Servicio Cliente realiza el seguimiento de las tareas y sabe si el proceso se ha completado sin problemas. El problema de este enfoque es que el Servicio de Cliente se puede convertir en una autoridad de gobierno central demasiado fuerte donde toda la lógica gira alrededor de él.
La estrategia a tomar con un mecanismo de coreografía sería informar a cada parte del sistema de su trabajo, y dejarlos trabajar en los detalles, como los bailarines en un ballet que se encuentran en su camino y reaccionan ante otros a su alrededor. Para ello el servicio cliente emitiría un mensaje asincrónico cuando se crea un cliente. Así el Servicio de Correo Electrónico, el Servicio Postal, y el Banco de puntos de Fidelidad, solo es necesario que se suscriban a esos eventos y reaccionar en consecuencia. Si algún otro servicio necesita llegar a la creación de un nuevo cliente, simplemente tiene que subscribirse a los eventos y hacer su trabajo cuando sea necesario. Este diseño implica que se necesita de trabajo adicional para asegurarnos de que se han sucedido las cosas correctas. Por ejemplo, saber si el banco de puntos de fidelidad tuvo un error y por alguna razón no estableció correctamente los puntos. Para solucionar este problema, normalmente es necesario crear un sistema de monitoreo que refleje explícitamente la vista del proceso de negocio, y a su vez, haga un seguimiento de lo que cada uno de los servicios realiza como entidad independiente que le permite ver excepciones mapeadas al flujo de proceso más explícito.
En general, los sistemas que utilizan el enfoque del tipo coreografía, están débilmente acoplados, son más flexibles y más susceptibles de cambiar. Sin embargo, es necesario realizar el trabajo extra de monitorear y seguir los procesos a través de los límites del sistema.
Críticas
El enfoque de microservicios esta sujeto a críticas por una serie de problemas:
- Los servicios forman barreras de información.
- Los mensajes entre servicios sobre la misma red tienen un costo mayor en términos de latencia y tiempo de procesamiento de mensajes comparado con un proceso de servidor monolítico.
- Las pruebas y el despliegue son más complicados en el modelo de microservicios.
- Mover responsabilidades entre servicios es difícil. Puede requerir comunicación entre distintos equipos, reescribir funcionalidades en diferentes lenguajes o cambiar la funcionalidad para que funcione en otra infraestructura.
- Contemplar el tamaño de los servicios como el principal mecanismo de estructuración puede llevar a utilizar demasiados servicios cuando una estrúctura de modularización interna puede llevar a un diseño más simple.
Carga cognitiva
La arquitectura introduce complejidad adicional y nuevos problemas a resolver, como latencia en la red, formato de los mensajes, equilibrado de carga y tolerancia a fallos.
Una aplicación compuesta por un número de microservicios tiene que acceder a su propio ecosistema, lo que puede crear complejidad innecesaria. Este tipo de complejidad puede reducirse estandarizando el mecanismo de acceso. La Web, considerada como un sistema, estandarizó el mecanismo de acceso a través de mantener el mismo sistema de acceso entre el navegador y los recursos de aplicación sobre los últimos 20 años. Utilizando el número de sitios web indexados por Google, se creció desde 26 millones de páginas en 1998 a alrededor de 60 trillones de páginas individuales en 2015 sin necesitar cambiar el mecanismo de acceso. La Web en sí misma es un ejemplo de cómo la complejidad inherente a un sistema tradicional de software monolítico puede superarse.
Véase también
En inglés: Microservices Facts for Kids