deployment - parte - ¿Cómo lidiar con estado compartido en una arquitectura de micro servicio?
spring cloud netflix (3)
Déjame intentar reformular el problema:
Actores:
- X: UserIds (estado de la cuenta)
- proporcionar servicio para obtener ID (basado en credenciales) y estado de la cuenta
- A: UserProfile
- Usando X para verificar el estado de una cuenta de usuario. Tiendas nombre junto con el enlace a la cuenta
- proporcionar servicio para obtener / editar nombre basado en ID
- B: UserBlogs
- Usando X de la misma manera. Almacena una publicación de blog junto con un enlace a la cuenta cuando el usuario escribe una
- Usar A para buscar una publicación de blog basada en el nombre de usuario
- proporcionar servicio obtener / editar lista de entradas de blog basadas en ID
- proporcionar servicio para buscar publicaciones de blog basadas en el nombre (depende de A)
- C: MobileApp
- envuelve las características de X, A, B en una aplicación móvil
- proporcione todos los servicios mencionados anteriormente, confiando en un contrato de comunicación bien definido con todos los demás (siguiendo la declaración @neleus)
Requisitos:
- El trabajo de los equipos X, A, B, C debe estar desacoplado
- Los entornos de integración para X, A, B, C necesitan actualizarse con las últimas características (para realizar pruebas de integración)
- Los entornos de integración para X, A, B, C necesitan tener un conjunto de datos "suficiente" (para realizar pruebas de carga y encontrar casos extremos)
Siguiendo la idea de @eugene: tener burlas para cada servicio provisto por cada equipo permitiría 1) y 2)
- el costo es más desarrollo de los equipos
- también mantenimiento de los simulacros, así como la característica principal
- El impedimento es el hecho de que tiene un sistema monolítico (todavía no tiene un conjunto de servicios limpios, bien definidos / aislados)
Solución sugerida:
¿Qué tal tener un entorno compartido con el conjunto de datos maestros para resolver 3)? Todos los ''servicios entregados'' (es decir, que se ejecutan en producción) estarían disponibles. Cada equipo podría elegir qué servicios usarían de aquí y cuál usarían de su propio entorno.
Una desventaja inmediata que puedo ver es los estados compartidos y la coherencia de los datos.
Consideremos las pruebas automatizadas ejecutadas contra los datos maestros, por ejemplo:
- B cambia los nombres (propiedad de A) para poder trabajar en su servicio de blog
- podría romper A, o C
- A cambia el estado de una cuenta para trabajar en algunos escenarios de permisos
- podría romper X, B
- C lo cambia todo en las mismas cuentas
- rompe todos los demás
El conjunto principal de datos se volverá rápidamente inconsistente y perderá su valor para el requisito 3) anterior.
Por lo tanto, podríamos agregar una capa ''convencional'' en los datos maestros compartidos: cualquiera puede leer todo el conjunto, pero solo puede modificar los objetos que han creado.
En nuestra compañía estamos pasando de una gran aplicación monolítica a una arquitectura de micro servicio. Los principales motivos técnicos para esta decisión fueron la necesidad de poder escalar los servicios de forma independiente y la escalabilidad del desarrollo: contamos con diez equipos de scrum trabajando en diferentes proyectos (o ''micro-servicios'').
El proceso de transición es fluido y ya hemos comenzado a beneficiarnos de las ventajas de estas nuevas estructuras técnicas y organizativas. Ahora, por otro lado, hay un punto principal de dolor con el que estamos luchando: cómo gestionar el ''estado'' de las dependencias entre estos micro servicios .
Pongamos un ejemplo: uno de los servicios de micro servicios con usuarios y registros. Este servicio (llamémoslo X) es responsable de mantener la información de identidad y, por lo tanto, es el proveedor principal de los ''id'' de usuario. El resto de los micro servicios tienen una fuerte dependencia de este. Por ejemplo, hay algunos servicios responsables de la información de perfil de usuario (A), permisos de usuario (B), grupos de usuarios (C), etc. que dependen de esos identificadores de usuario y, por lo tanto, existe la necesidad de mantener cierta sincronización de datos entre estos servicios (es decir, el servicio A no debe tener información para un usuario no registrado en el servicio X). Actualmente mantenemos esta sincronización al notificar los cambios de estado (nuevos registros, por ejemplo) usando RabbitMQ.
Como se puede imaginar, hay muchas X: muchos servicios "principales" y muchas dependencias más complicadas entre ellos.
El problema principal viene cuando se manejan los diferentes entornos de desarrollo / prueba. Cada equipo (y, por lo tanto, cada servicio) necesita pasar por varios entornos para poner en funcionamiento algún código: integración continua, integración de equipos, prueba de aceptación y entornos en vivo.
Obviamente, necesitamos todos los servicios que trabajen en todos estos entornos para verificar que el sistema funcione como un todo. Ahora, esto significa que para probar los servicios dependientes (A, B, C, ...) no solo debemos confiar en el servicio X, sino también en su estado. Por lo tanto, necesitamos de alguna manera mantener la integridad del sistema y almacenar un estado global y coherente .
Nuestro enfoque actual para esto es obtener instantáneas de todas las bases de datos del entorno real, realizar algunas transformaciones para reducir el tamaño y proteger la privacidad de los datos y su propagación a todos los entornos antes de realizar pruebas en un entorno particular. Obviamente, se trata de una tremenda sobrecarga, tanto organizativa como de recursos computacionales: tenemos diez entornos de integración continuos, diez entornos de integración y un entorno de prueba de aceptación que todos deben actualizarse con estos datos compartidos de Live y la última versión del código frecuentemente.
Estamos luchando por encontrar una mejor manera de aliviar este dolor. Actualmente estamos evaluando dos opciones:
- usando contenedores tipo Docker para todos estos servicios
- tener dos versiones de cada servicio (una destinada para el desarrollo de ese servicio y la otra como un sandbox para ser utilizado por el resto de los equipos en sus pruebas de desarrollo e integración)
Ninguna de estas soluciones alivia el dolor de los datos compartidos entre servicios . Nos gustaría saber cómo otras empresas / desarrolladores están abordando este problema, ya que creemos que esto debe ser común en una arquitectura de micro servicios.
¿Cómo lo están haciendo? ¿También tienes este problema? ¿Alguna recomendacion?
Perdón por la larga explicación y muchas gracias!
Desde mi perspectiva, solo los objetos usan los servicios deberían tener el estado. Consideremos su ejemplo: el servicio X es responsable del ID del usuario, el servicio A responsable de la información del perfil, etc. Supongamos que el usuario Y que tiene un token de seguridad (que puede crearse, por ejemplo, usando su nombre de usuario y contraseña) debe ser único) entradas al sistema. Luego, el cliente, que contiene la información del usuario, envía el token de seguridad al servicio X. El servicio X contiene información sobre el ID de usuario vinculado a dicho token. En el caso del nuevo usuario, el servicio X crea la nueva ID y almacena su token. Luego, el servicio X devuelve ID al objeto del usuario. El objeto de usuario le pregunta al servicio A sobre el perfil de usuario proporcionando una identificación de usuario. El servicio A toma la identificación y le pregunta al servicio X si esa identificación existe. El servicio X envía la respuesta positiva, luego el servicio A puede buscar la información de perfil por ID de usuario o pedirle al usuario que proporcione dicha información para crearla. La misma lógica debería funcionar con los servicios B y C. Tienen que hablar entre ellos pero no necesitan saber sobre el estado del usuario.
Pocas palabras sobre los ambientes. Sugeriría usar títeres . Esta es la forma de automatizar el proceso de implementación del servicio. Estamos utilizando los títeres para implementar los servicios en los diferentes entornos. El script de títeres es de alcance y permite una configuración flexible.
Esta vez he leído su pregunta desde otra perspectiva, así que aquí hay una "opinión diferente". Sé que puede ser demasiado tarde, pero espero que ayude con un mayor desarrollo.
Parece que el shared state
es el resultado de un desacoplamiento incorrecto. En la arquitectura de microservicios ''correcta'', todos los microservicios deben estar aislados funcionalmente en vez de lógicamente. Me refiero a que la información de los tres user profile information (A), user permissions (B), user groups (C)
parecen funcionalmente iguales y más o menos funcionalmente coherentes. Parecen ser un user microservice
único con un almacenamiento coherente. No veo aquí ninguna razón para desacoplarlos (o al menos no has dicho nada sobre ellos).
Entonces, el problema real está relacionado con el aislamiento de microservicio . Idealmente, cada microservicio puede vivir como un producto independiente completo y ofrecer un valor comercial bien definido. Al elaborar la arquitectura del sistema, la dividimos en pequeñas unidades lógicas (A, B, C, etc. en su caso, o incluso más pequeñas) y luego definimos subgrupos funcionalmente coherentes. No puedo decirte las reglas exactas de cómo hacerlo, tal vez algunos ejemplos. Comunicaciones / dependencias complejas entre las unidades, muchos términos comunes en sus lenguajes ubicuos, por lo que parece que esas unidades pertenecen al mismo grupo funcional y, por lo tanto, al microservicio.
Por lo tanto, de su ejemplo, dado que hay un solo almacenamiento, solo tiene forma de gestionar su coherencia como lo hizo.
Por cierto, me pregunto ¿de qué manera real has resuelto tu problema? Además, si te gusta mi idea, no dudes en aceptarla.