El dolor y el sufrimiento al depurar microservicios en el desarrollo web

En TI, rara vez se ve a una persona que no haya oído hablar de microservicios. Hay muchos artículos en Internet y en sitios especializados sobre este tema, que generalmente explican bien las diferencias entre el monolito y, de hecho, los microservicios. Un desarrollador de Java sin experiencia, que ha leído artículos de la categoría "Qué son los microservicios para aplicaciones web y con qué comen", está lleno de alegría y confianza de que ahora todo será maravilloso. Después de todo, el objetivo principal es "ver a través" el monstruoso monolito (el artefacto final, que, como regla, es un archivo de guerra / oído), que realiza un montón de todo, en una serie de servicios vivos por separado, cada uno de los cuales realizará una función estrictamente definida relacionada solo con él, y lo haremos bien Además de esto viene la escalabilidad horizontal, solo escalenodos correspondientes, y todo será genial. Han llegado más usuarios o se requieren más capacidades, solo se agregaron 5-10 nuevas instancias de servicio. En términos generales, en general, así es como funciona, pero, como saben, el diablo está en los detalles, y lo que inicialmente parecía bastante simple, después de un examen más detallado, puede convertirse en problemas que nadie tuvo en cuenta inicialmente.

En esta publicación, los colegas de la práctica Java de Rexoft comparten sus experiencias sobre cómo depurar microservicios para la web.



Cómo lograr la integridad de los datos transaccionales


Al tratar de transferir la arquitectura de un monolito a microservicios, los equipos que no tenían esa experiencia antes a menudo comienzan a dividir los servicios en objetos de nivel superior del modelo de dominio, por ejemplo: Usuario / Cliente / Empleado , etc. En el futuro, con un estudio más detallado, aparece la comprensión, lo cual es más conveniente dividir en bloques más grandes que agregan varios objetos del dominio de dominio dentro de sí mismos. Debido a esto, puede evitar llamadas innecesarias a servicios de terceros.

El segundo punto importante es el soporte para la integridad de los datos transaccionales. En el monolito, este problema se resuelve a través del Servidor de aplicaciones, donde está girando war / ear, dentro del cual el contenedor, de hecho, describe los límites de las transacciones. En el caso de los microservicios, los límites de las transacciones son borrosos y existe la necesidad, además de escribir código de lógica de negocios, para poder administrar la integridad de los datos y mantener su consistencia entre las diferentes partes del sistema. Esta es una tarea bastante no trivial. Las recomendaciones para resolver este tipo de problema arquitectónico se pueden encontrar en Internet y en las comunidades técnicas relevantes.

En este artículo, trataremos de describir las dificultades técnicas específicas que surgen cuando los equipos intentan trabajar con microservicios y las formas de resolverlos. Noto de inmediato que las opciones propuestas no son las únicas verdaderas. Quizás haya servicios más elegantes, pero las recomendaciones que daré se prueban en la práctica y resuelven con precisión las dificultades existentes, y si usarlas o no es un asunto personal para todos.

El principal problema con los microservicios es que son extremadamente fáciles de ejecutar localmente (por ejemplo, usando spring.io e intellij idea , esto se puede hacer en solo 5 minutos, o incluso menos). Sin embargo, al intentar hacer lo mismo en Kubernetesel clúster (si tenía poca experiencia con él antes), un simple lanzamiento del controlador que imprime "Hello World" al acceder a un punto final específico puede llevar medio día. En el caso de un monolito, la situación es más simple. Cada desarrollador tiene un servidor de aplicaciones local. El proceso de implementación también es bastante simple: debe copiar el artefacto final de guerra / oreja en el lugar correcto en el servidor de aplicaciones manualmente o utilizando el IDE . Por lo general, esto no es un problema.

Depuración de matices


El segundo punto importante es la depuración . En situaciones con un monolito, se supone que el desarrollador tiene un servidor de aplicaciones en su máquina, en el que se despliega su war / ear. Siempre puede depurar, porque todo lo que necesita está a la mano. Con los microservicios, todo es un poco más complicado, un servicio suele ser algo en sí mismo. Como regla general, tiene su propio esquema de base de datos, en el que se encuentran sus datos, realiza funciones específicas específicas para él, toda la comunicación con otros servicios se organiza a través de llamadas HTTP sincrónicas (por ejemplo, a través de RestTemplate o Feign), asíncrona (por ejemplo, Kafka o RabbitMQ). Por lo tanto, la tarea esencialmente simple de guardar o validar un determinado objeto que se implementó previamente en un lugar, dentro de un solo archivo war / ear, en el caso general con un enfoque de microservicio, se vuelve representable en la forma: vaya a uno o N servicios adyacentes, ya sean operaciones de adquisición de datos , por ejemplo, algunos valores de referencia o la operación de guardar entidades adyacentes,cuyos datos son necesarios para realizar la lógica empresarial en nuestro servicio. Escribir lógica de negocios en este caso se vuelve mucho más difícil.

En consecuencia, las opciones de solución son las siguientes :

  1. Escribe tu código de lógica de negocios. Al mismo tiempo, todas las llamadas externas se burlan : los contratos externos se emulan, las pruebas se escriben bajo los supuestos de que los contratos externos son así, después de eso hay un despliegue en el circuito para su verificación. A veces es afortunado y la integración funciona de inmediato, a veces es desafortunado: tiene que rehacer el código de lógica de negocios una enésima vez, porque durante el tiempo que implementamos la funcionalidad, el código en el servicio adyacente se actualizó, las firmas de API cambiaron y necesitamos rehacerlo parte de la tarea está de su lado.
  2. . , , Kubernetes, . . , — , remote debug . , runtime , , . -, , 2–5 , . . , Kubernetes , . -, (Per thread), , .

Kubernetes


Una solución a este problema, de hecho, es la telepresencia . Probablemente hay otros programas de este tipo, pero la experiencia personal fue solo con él, y se estableció positivamente. En general, el principio de funcionamiento es el siguiente:

en la máquina local, el desarrollador instala la telepresencia , configura kubectl para acceder al clúster Kubernetes correspondiente (agrega la configuración de bucle a ~ / .kube / config ). Después de eso, comienza la telepresencia , que de hecho actúa como un proxy entre la computadora del desarrollador local y Kubernetes. Hay diferentes opciones de inicio, es mejor mirar con más detalle en la guía oficial, pero en el caso más básico se reduce a dos pasos:

  1. Sudo telepresence (, Linux- , sudo . , root/). Kubernetes deployment telepresence . deployment Kubernetes.
  2. Iniciar su instancia de servicio es como de costumbre en la computadora local del desarrollador. Sin embargo, en este caso, tendrá acceso a toda la infraestructura del clúster de Kubernetes, ya sea Service Discovery (Eureka, Consul), Api Gateway (Zuul), Kafka y sus colas, si las hay, y así sucesivamente. Es decir, de hecho, tenemos acceso a todo el entorno de clúster que necesitamos, pero a nivel local. La ventaja es la posibilidad de depuración local, pero en un entorno de clúster, y ya será mucho más rápido, porque, de hecho, estamos dentro de Kubernetes (a través del túnel) y no accedemos desde el exterior a través del puerto para la depuración remota.

Esta solución tiene varias desventajas:

  1. Telepresence Linux Mac, Windows VFS, , issue GitHub. . , - Linux/Mac, .
  2. , Service Discovery (Eureka, Consul)Round Robin , endpoint , , , :

  • kubernetes -> . telepresence deployment , «» Eureka ip-address:port/service-name dns-name:port/service-name , . . Kubernetes , timeout;
  • deployment - Kubernetes , ( ) (Round Robin), ;
  • endpoint, feature, HTTP 404 endpoint Gateway, Service Discovery , Round Robin . Service Discovery endpoint , HTTP 404.
  • , , .


Por enrutamiento dinámico de una solicitud, queremos decir que API Gateway (Zuul) tiene la capacidad de elegir entre varias instancias del mismo servicio que necesitamos. En el caso general, este problema se puede resolver agregando un predicado que le permita seleccionar el servicio deseado del grupo de servicios común con el mismo nombre en la etapa de procesamiento de la solicitud. Naturalmente, cada servicio entre aquellos con los que queremos poder enrutar dinámicamente, tendrá que tener algún tipo de metainformación que contenga datos que se utilizarán para determinar si este servicio es necesario o no. Spring Cloud (en el caso de Eureka), por ejemplo, le permite hacer esto especificando en un bloque de metadatos especial en application.yml :

eureka:
  instance:
    preferIpAddress: true
    metadata-map:
      service.label: develop

Después de registrar dicho servicio en Service Discovery en su com.netflix.appinfo.InstanceInfo # getMetadata , habrá una etiqueta con la clave service.label y el valor desarrollado , que se puede obtener en tiempo de ejecución. Un punto importante en la etapa de inicio de un servicio es verificar si existe una instancia de servicio en Service Discovery con dicha metainformación o no para evitar posibles colisiones.

Opciones de enrutamiento


Después de eso, la solución del problema se puede reducir a dos opciones:

  1. API Gateway . , , , , Headers: DestionationService: feature/PRJ-001. , , Header . , — - API Gateway.
  2. API Gateway, , . ., , , Zuul 1 endpoint- /api/users/… user, feature/PRJ-001, Zuul 2 endpoint- /api/users/… user, feature/PRJ-002. , N API Gateway N , . . , . . feature — , , , , , , . API Gateway, , . ., , , — , .






Como parte de la API de Gateway, también vale la pena proporcionar un mecanismo que le permita cambiar las reglas de enrutamiento en tiempo de ejecución. Es mejor colocar esta configuración en config-map . En este caso, será suficiente reescribir las nuevas rutas y reiniciar la API de Gateway en Kubernetes para actualizar el enrutamiento, o usar el actuador Spring Boot (siempre que exista una dependencia correspondiente en la API de Gateway): llamar al punto final / actualizar, que esencialmente vuelve a leer datos de config-map y actualizarán las rutas.

Un punto importante también es que debería haber, relativamente hablando, una instancia de referencia del servicio (por ejemplo, etiquetada desarrollar, que se recopilará de la rama principal del desarrollo del servicio) y una API de puerta de enlace principal independiente, que siempre se especificará en la configuración para acceder a este servicio. En esencia, nos proporcionamos un entorno de preparación independiente que siempre estará operativo en el contexto del enrutamiento dinámico.

Un ejemplo de un bloque de mapa de configuración para la API de Gateway que contiene configuraciones para el enrutamiento (aquí es solo un ejemplo de cómo podría verse, para una operación adecuada requiere un enlace correspondiente en forma de código en el lado del back-end del servicio API Gateway) :

{
  "kind": "ConfigMap",
  "apiVersion": "v1",
  "metadata": {
    ...
  },  
"data": {
    ...        
    "rules.meta.user": "develop",
    "rules.meta.client": "develop",
    "rules.meta.notification": "feature/PRJ-010",
    ...    
  }
}

rules.meta es un mapa que contiene reglas de enrutamiento para servicios.
usuario / cliente / notificación : el nombre del servicio bajo el cual está registrado en Eureka.

development / feature / PRJ-010 : etiqueta de servicio de application.yml del servicio correspondiente, en función del cual se seleccionará el servicio deseado entre todos los servicios disponibles con el mismo nombre de Service Discovery , si hay más de una instancia de dicho servicio.

Conclusión


Como todo en este mundo, las herramientas y soluciones en TI no son perfectas. No piense que si cambia la arquitectura, todos los problemas terminarán de una vez. Solo una inmersión detallada en las tecnologías utilizadas y su propia experiencia le brindarán una imagen real de lo que está sucediendo.

Espero que este material te ayude a resolver tu problema. Tareas interesantes y vender sin errores!

All Articles