Continuando con el estudio del tema de los microservicios , decidimos ofrecerle una traducción de un artículo sobre pruebas de MOA (aplicaciones orientadas a microservicios). Recientemente, ya hemos abordado el tema de las pruebas , pero en el caso de los microservicios, las pruebas unitarias regulares no son suficientes, también debe considerar aspectos relacionados con CI / CD y otras cosas que se discutirán en este artículo.
Los microservicios son un nuevo estilo de arquitectura de software que se utiliza en la creación de sistemas distribuidos y se están implementando cada vez más en empresas que operan en la Web, incluidas las más grandes. Por ejemplo, Netflix y Amazon han adoptado una arquitectura de microservicios para acelerar significativamente el lanzamiento de productos de software. Al mismo tiempo, los sistemas monolíticos más antiguos no pueden escalar a una velocidad que satisfaría los desafíos modernos.
Pero los beneficios que puede aportar una arquitectura de microservicio son tan buenos como se puedan probar las mejores prácticas para respaldarla. Al diseñar pruebas para validar sistemas de microservicios, donde la tasa de liberación puede describirse como increíblemente rápida, se requiere un pensamiento innovador, tanto a nivel micro como macro.
A continuación, exploraremos los tres tipos de aplicaciones orientadas a microservicios, discutiremos algunos de los desafíos que deberá abordar para probarlos y hablaremos sobre cómo se escriben las pruebas para apreciar plenamente los beneficios de los microservicios.
Brevemente sobre microservicios
Antes de entrar en algunas de las complejidades del diseño de pruebas para microservicios, echemos un vistazo a qué son los microservicios. El microservicio es un componente de software detallado al que se le ha dado una definición semántica clara; sin embargo, el microservicio lleva su propio conjunto de datos y no depende de otras estructuras y fuentes de datos. Cada microservicio tiene su propio ciclo de implementación, por lo que después de que se hayan realizado cambios en el microservicio, puede liberarlo sin interrumpir el trabajo de cualquier otro microservicio en el alcance de la aplicación con la que se ejecuta este microservicio.
Ventajas de los microservicios sobre las aplicaciones monolíticas
Además, echemos un vistazo a lo que no es un microservicio. La siguiente figura muestra un ejemplo de una aplicación monolítica típica.
Figura: 1: Las arquitecturas de aplicaciones monolíticas suelen tener una fuerte cohesión de componentes
Los componentes Clientes, Productos, Pedidos y (Comentarios) se pueden considerar como un conjunto de clases escritas en un lenguaje orientado a objetos como Java o C #, donde los clientes son una matriz de objetos correspondientes a clientes, productos, una matriz. de objetos correspondientes a productos, etc. El objeto de cliente puede utilizar tanto el objeto de cliente como el objeto de producto. O, debido al hecho de que todos los componentes de una aplicación monolítica acceden a la misma base de datos, el objeto de pedido puede acceder directamente a la base de datos y obtener toda la información sobre el producto y el pedido que se necesita para completar las tareas. El acceso directo a la base de datos no solo es posible, y cuánto contradice el espíritu de la programación orientada a objetos basada en un mapeo relacional de objetos. ORM , pero también se usa activamente, especialmente en aquellas áreas donde hay una gran demanda de funciones listas para usar en la fecha acordada, sin importar el costo.
El resultado es un sistema estrechamente acoplado, a veces frágil, donde el lanzamiento de una nueva versión de una aplicación requiere una coordinación significativa entre todos los equipos de desarrollo involucrados en el desarrollo de ese sistema. Debido al fuerte acoplamiento, todo el ciclo de lanzamiento no puede ir más rápido que el trabajo más lento de renegociar una dependencia. En otras palabras, si es el momento de actualizar la aplicación y agregarle una nueva característica por parte de los clientes y una nueva característica por producto, entonces el lanzamiento no se llevará a cabo hasta que ambas características estén listas. Si se necesita un día para realizar una actualización de producto y tres semanas para realizar un cambio de cliente, entonces el lanzamiento se llevará a cabo no antes de tres semanas. Además, lo que agrava aún más la situación, durante el lanzamiento, puede ser necesario no solo coordinar el nuevo código en relación con los clientes y las mercancías,pero también realizar cambios en el esquema de la base de datos, y esto también deberá gestionarse durante el proceso de lanzamiento.
Publicar cambios en una base de datos es una empresa muy delicada. Siempre existe el riesgo de efectos secundarios no deseados al cambiar la estructura de la base de datos. Recuerde que si algún componente puede acceder a la base de datos, accederá a ella e incluso realizará operaciones sobre los datos que no están dentro del alcance de este componente. Tal "manipulación fuera del área" puede conducir a fallas de otros componentes, y tales fallas pueden pasar desapercibidas hasta que ocurre un desastre. A veces, se puede encontrar un peligro potencial en el código hasta el lanzamiento de este código en producción. Desafortunadamente, esto sucede todo el tiempo.
Algunas empresas están dispuestas a soportar los ciclos de liberación lentos que son típicos para trabajar con aplicaciones monolíticas. Pero, si estamos hablando de una empresa que brinda soporte a cientos de miles de usuarios, y al mismo tiempo debe mantener miles de componentes en funcionamiento, entonces el ritmo "no más rápido que el lanzamiento del componente más tardío" es inaceptable. .
Aplicaciones orientadas a microservicios
En una aplicación orientada a microservicios (MOA), cada componente funcional se descompone en las unidades más pequeñas posibles con una funcionalidad pronunciada (dentro de límites razonables). En este caso, cada uno de estos componentes funcionales se organiza como un microservicio. Cada empresa tiene su propio bagaje de códigos heredados, así como una cultura corporativa a la que se debe adherir, por lo que es imposible escribir "notas similares" sobre cómo se debe realizar exactamente la descomposición.
Cuando se trata de descomponer una aplicación monolítica en MOA, la clave a recordar es que lo mejor es enemigo de lo bueno. Asegúrese de que cada microservicio esté estructurado de acuerdo con su definición semántica, que el microservicio tenga sus propios datos y su propio ciclo de lanzamiento. La profundidad de la implementación depende de lo que la empresa pueda pagar, en función del tiempo disponible, la experiencia de los empleados y los recursos disponibles. Algunos microservicios pueden tener solo una función, otros pueden tener muchas funciones.
Hay tres tipos de MOA : sincrónico , asincrónico e híbrido .
Aplicaciones síncronas orientadas a microservicios
La figura 2 muestra un MOA sincrónico. La comunicación entre servicios es un principio de solicitud-respuesta típico de las interacciones HTTP en la web.
Figura 2: Aplicación orientada a microservicios basada en comunicación síncrona entre servicios
Cada microservicio está segmentado detrás del servidor HTTP, lo que permite acceder a la lógica de este microservicio. Un microservicio específico solo conoce su propia área de responsabilidad; también puede conocer la interfaz para interactuar con otro microservicio, pero no puede ver la lógica interna de otro servicio. Además, el microservicio solo conoce sus propios datos. Los almacenes de datos de otros microservicios no son conocidos por él y no están disponibles para él. Es imposible "secuestrar" los datos de otro microservicio accediendo directamente al almacén de datos. La única forma de obtener datos del microservicio y transferirle nuevos datos es interactuar con él a través de la interfaz pública.
Entre las ventajas de los MOA síncronos está su independencia. Por ejemplo, el microservicio de Clientes que se muestra arriba puede actualizarse en cualquier momento que le convenga. No hay dependencias externas que deban ajustarse a esto. Siempre que el microservicio no cambie su interfaz pública, así como la estructura de los datos que pretende consumir de la solicitud y devolver como respuesta, el riesgo de romper toda la arquitectura del MOA es mínimo.
Por su propia naturaleza, la arquitectura MOA es tal que mantiene su propia integridad estructural. Dado que el microservicio en sí mismo transporta sus propios datos, su trabajo no afecta los almacenes de datos de otros servicios. Dado que un microservicio se representa como un conjunto de URL HTTP y estructuras de datos relacionadas, construido sobre un principio de solicitud-respuesta, los límites de la interfaz del microservicio están claramente definidos.
Los MOA sincrónicos son cada vez más populares. Muchas interfaces MOA síncronas se basan en el paradigma REST, un estilo que existe desde 2000del año. Pero, a pesar de su popularidad, MOA tiene un inconveniente: baja velocidad. Los consumidores de un microservicio sincrónico nunca se ejecutarán más rápido de lo que este microservicio puede manejar solicitudes y respuestas. Esto puede ser un cuello de botella importante, especialmente cuando se trata de microservicios, cuyos procesos tardan mucho en ejecutarse. Un ejemplo es un servicio analítico complejo que consume y procesa terabytes de datos. Pocos clientes aceptarán sentarse y esperar unos minutos seguidos hasta que se cierre el servicio. Sería más conveniente decirle al microservicio qué trabajo debe realizarse y luego recibir una notificación cuando los resultados estén listos.
En situaciones como esta, es más conveniente adoptar un enfoque asincrónico para diseñar microservicios.
Aplicaciones orientadas a microservicios asincrónicos
La Figura 3 a continuación muestra una implementación de MOA asincrónica, donde la comunicación de servicio a servicio se proporciona mediante el intercambio de mensajes entre las partes involucradas. Normalmente, esta interacción se denomina "disparar y olvidar".
Figura 3: La arquitectura de MOA asincrónica se basa en la mensajería
En la arquitectura de MOA asincrónica, los mensajes se pueden generar de forma aleatoria o en respuesta a un evento determinado. Por ejemplo, cuando (como se muestra en la imagen de arriba) se crea un pedido en el microservicio Pedidos, este servicio puede publicar un evento
orders_created
y colocarlo en una cola de mensajes complementarios ., que está escuchando otro microservicio, facturando (no se muestra en la figura) y aceptando mensajes entrantes. El microservicio de facturación recoge el mensaje de información del pedido y lo procesa de una manera que es relevante desde una perspectiva de facturación.
La ventaja de un enfoque asincrónico para diseñar aplicaciones orientadas a microservicios es que no existen cuellos de botella en dicho sistema y, por lo tanto, es extremadamente eficiente. La desventaja es que dicho sistema es muy difícil de crear y luego administrar.
Para sistemas asincrónicos a gran escala como Uber, es normal procesar miles de mensajes por segundo. La depuración de un sistema de este tipo es difícil porque sus flujos de trabajo no tienen trayectorias claras. No se trata de hacer una acción primero y luego hacer otra; la lógica se dispara en función del mensaje recibido, es decir, puede pasar cualquier cosa en cualquier momento.
Esto debe tenerse en cuenta al desarrollar estrategias de prueba. Por ejemplo, un sistema asincrónico no es práctico, cuyo rendimiento depende de los tiempos de solicitud y respuesta, ya que nunca habrá una correspondencia uno a uno "una solicitud-una respuesta".
Aplicaciones híbridas orientadas a microservicios
El enfoque equilibrado para implementar una aplicación orientada a microservicios es híbrido. Los servicios son simultáneamente síncronos, es decir, se admite la comunicación directa de solicitud-respuesta entre ellos y, al mismo tiempo, asincrónica; es decir, algunos de los mensajes se generan junto con la mensajería síncrona o como resultado de ella.
La Figura 4 ilustra los patrones de comunicación utilizados en un enfoque híbrido para aplicaciones orientadas a microservicios. El microservicio de Clientes se representa como una URL asociada con un servidor HTTP y como una cola en un agente de mensajes.
Figura 4: Un enfoque híbrido para diseñar aplicaciones orientadas a microservicios que utiliza opciones de comunicación tanto síncronas como asincrónicas entre servicios y consumidores.
Puede agregar un cliente al microservicio mediante la comunicación de solicitud-respuesta HTTP estándar. La solicitud se recibe y procesa, luego se genera una respuesta. Sin embargo, antes de que se genere una respuesta, se publica un mensaje con información sobre el nuevo cliente en la cola de mensajes complementarios para que otros servicios interesados puedan consumirlo. La información enviada a la cola de mensajes puede ser la misma que la generada en la respuesta HTTP, o ligeramente diferente, a discreción del microservicio. Todo depende de cómo esté diseñado el microservicio y de los acuerdos de QoS que el microservicio debe hacer cumplir.
GraphQLEs una tecnología API que admite la comunicación sincrónica y asincrónica entre un consumidor y un servicio. Obtenga más información sobre esta técnica y las suscripciones GraphQL aquí .
La característica más importante del enfoque híbrido es que combina los méritos de los otros dos enfoques. Pero existen desventajas acompañantes, en particular, posibles cuellos de botella que reducen el rendimiento y la complejidad adicional asociada con la necesidad de admitir dos tipos de interfaces fundamentalmente diferentes utilizados por el microservicio "dentro" y "fuera".
Surgen dificultades al diseñar pruebas para microservicios
Cuando se trata de probar estas aplicaciones, lo primero que hay que recordar es que es muy raro que varios MOA compartan un solo microservicio. Normalmente, un microservicio es uno de muchos en un dominio específico dentro de una aplicación. La virtud de un enfoque orientado a microservicios para el diseño de la arquitectura de aplicaciones es una transferencia de código más rápida hacia y desde la producción. Netflix, por ejemplo, tiene más de 4.000 implementaciones por día ). Esta tasa de publicación simplemente no es posible en un entorno de aplicación monolítico tradicional.
También debe recordarse que las pruebas de MOA deben ocurrir tanto a nivel micro como macro.
Prueba de micro nivel
A nivel micro, cada servicio debe probarse minuciosamente dentro de su área de responsabilidad. Según algunas interpretaciones, una función se considera un límite de microservicio.
Vaya más allá de las pruebas unitarias tradicionales
Es recomendable prestar especial atención a la función dentro del microservicio, dada la creciente popularidad del paradigma sin servidor , según el cual el microservicio solo debe presentarse como una única función. Pero muchos probadores en práctica tienden a limitarse a pruebas unitarias cuando trabajan con microservicios. Mientras que una prueba unitaria normalmente cubre una sola función que funciona en un entorno de prueba muy limitado, un microservicio está diseñado para servir a una audiencia en la web. Por lo tanto, las condiciones de prueba deben ser extremas.
Por ejemplo, como parte de una buena prueba a nivel micro, puede ejecutar cien mil instancias de un microservicio al mismo tiempo y ver cómo se comportan en esa escala. No es suficiente probar solo una función a la vez aplicándole una sola prueba unitaria. Es necesario ejecutar tales pruebas en miles de instancias de la función al mismo tiempo, teniendo en cuenta los indicadores del entorno de alojamiento en el que tendrá que trabajar.
Pruebe su unidad de implementación
Además de probar la funcionalidad del microservicio, también debe encargarse de probar la unidad de implementación dentro de la cual se lanza el microservicio. Normalmente, los microservicios se implementan como contenedores como parte de una tecnología de orquestación como Kubernetes o Docker Swarm . Un aspecto importante de la orquestación de contenedores es garantizar la resistencia a largo plazo de los microservicios.
Se cree que los microservicios pueden fallar por una variedad de razones, tanto debido a fallas del host como a errores en el microservicio mismo. Es bastante normal que durante el funcionamiento simultáneo de miles de contenedores se produzca algún tipo de mal funcionamiento actual. La importancia de probar es que la salida correcta del microservicio del juego no es menos importante que su correcto funcionamiento. Las pruebas de micro-nivel aseguran que el microservicio cobrará vida y morirá perfectamente, a cualquier escala del sistema.
Asegúrese de que se registre absolutamente todo lo que le sucede
Las pruebas también deben garantizar que todos los eventos importantes que ocurren en el microservicio se registren correctamente y, lo que es más importante, estas entradas de registro se pueden comprender. En el mundo de los microservicios, el registro es muy importante, especialmente para MOA asincrónico donde no hay ejecución secuencial de comportamientos. A menudo, solo los datos de registro le ayudarán a comprender lo que está sucediendo en la aplicación.
Prueba de macros
Ya sea que el MOA sea síncrono, asíncrono o híbrido, se muestra en modo de prueba riguroso. Al probar microservicios a nivel macro, debe asegurarse de que hay dos aspectos para ser satisfactorios: la comunicación entre servicios y los procesos de implementación.
Garantizar una comunicación inteligente entre servicios
Los microservicios son, por definición, independientes entre sí. Ellos determinan qué hacer en función de la información que reciben, por lo que la precisión de la comunicación entre servicios es fundamental para el funcionamiento integral de una aplicación orientada a microservicios.
Brindar comunicación entre servicios significa asegurarse de que la información correcta llegue a donde debe ir y a donde va. Las pruebas deben poder rastrear cómo se intercambian los mensajes y cómo se procesan. Esto es cierto tanto para la comunicación de solicitud-respuesta HTTP como para el intercambio de mensajes asíncronos propagados a través de un intermediario de mensajes. Las pruebas deberían ayudar a garantizar que los formatos de mensajes sean compatibles para garantizar una ruta de sistema positiva., y los mensajes con formato incorrecto son rechazados y con las explicaciones suficientes (y no solo con un error de "mensaje incorrecto").
Prueba de la integración continua y la implementación continua
Los procesos de integración continua y despliegue continuo (CI / CD) bien organizados son esenciales en cualquier paradigma de desarrollo de software, pero cuando se trata de aplicaciones de microservicios, un proceso de CI / CD eficiente, preciso y rápido es fundamental. Los MOA se pueden revisar a una velocidad de cientos de actualizaciones diarias, por lo que un solo microservicio que se compila tarde puede convertirse en un cuello de botella y ralentizar todo el proceso de lanzamiento.
La mejor manera de ir a lo seguro es dar a las pruebas de canalización de CI / CD la misma prioridad que cualquier otro modo de prueba de alto nivel. Identificar y solucionar problemas como microservicios de compilación lenta debido a artefactos en el código, aprovisionamiento lento de tiempos de ejecución donde se alojan microservicios y aumento lento de microservicios ya implementados son requisitos previos para una canalización de CI / CD saludable. Incluso si el microservicio es tan complejo como un transbordador, será de poca utilidad si no puede desplegar y desplegar rápidamente unidades operativas.
Conclusión
Los microservicios son el comodín real. Las aplicaciones orientadas a microservicios brindan la flexibilidad y la velocidad necesarias para poner nuevas funciones en línea casi a la velocidad del rayo. Las grandes empresas que dan soporte a millones de usuarios lo han aprendido hoy, pero cada día más y más empresas adoptan este estilo arquitectónico cuando sus aplicaciones alcanzan la escala de toda la web.
Si bien muchas empresas están tratando seriamente de incorporar el espíritu de una arquitectura orientada a microservicios en sus procesos de desarrollo, estos MOA a menudo se prueban utilizando prácticas originalmente pensadas para aplicaciones monolíticas. Este es un enfoque miope.
Por el contrario, las empresas deben aprender técnicas de prueba modernas diseñadas para garantizar la independencia de los microservicios y la agilidad de las aplicaciones que utilizan estos microservicios. Cuando los equipos de desarrollo y los evaluadores logran sincronizar los principios detrás del diseño de aplicaciones de microservicios, toda la empresa puede confiar mucho más en las fortalezas de los microservicios.