¡Hola a todos! Tenemos una gran noticia, OTUS lanza nuevamente el curso en junio
Si te has encontrado con todo este asunto de los microservicios sin ningún contexto, se te perdonará que pienses que es un poco extraño. Dividir una aplicación en fragmentos conectados por una red significa necesariamente agregar modos complejos de tolerancia a fallas al sistema distribuido resultante.
Aunque este enfoque implica dividirlo en muchos servicios independientes, el objetivo final es mucho más que simplemente ejecutar esos servicios en diferentes máquinas. Estamos hablando aquí de interacción con el mundo exterior, que también es común en su esencia. No en el sentido técnico, sino más bien en el sentido de un ecosistema que consta de muchas personas, equipos, programas, y cada una de estas partes de alguna manera necesita hacer su trabajo.
Las empresas, por ejemplo, son un conjunto de sistemas distribuidos que colectivamente contribuyen al logro de algún objetivo. Hemos ignorado este hecho durante décadas, intentando lograr la unificación enviando archivos FTP o utilizando herramientas de integración empresarial mientras nos centramos en nuestros propios objetivos aislados. Pero con la llegada de los servicios todo cambió. Los servicios nos han ayudado a mirar más allá del horizonte y ver un mundo de programas interdependientes que funcionan juntos. Sin embargo, para trabajar con éxito, es necesario reconocer y diseñar dos mundos fundamentalmente diferentes: el mundo externo, donde vivimos en un ecosistema de muchos otros servicios, y nuestro mundo personal e interno, donde gobernamos solos.
Este mundo distribuido es diferente de aquel en el que crecimos y al que estamos acostumbrados. Los principios de la construcción de la arquitectura monolítica tradicional no resisten la crítica. Por lo tanto, lograr que estos sistemas sean correctos es algo más que crear un diagrama de pizarra atractivo o una prueba de concepto interesante. La cuestión es garantizar que dicho sistema funcione con éxito durante un largo período de tiempo. Afortunadamente, los servicios existen desde hace bastante tiempo, aunque tienen un aspecto diferente.
Así que hoy veremos cómo han cambiado las reglas, por qué necesitamos repensar la forma en que abordamos los servicios y los datos que se transmiten entre sí, y por qué necesitaremos herramientas completamente diferentes para hacerlo.
La encapsulación no siempre será tu amiga
Los microservicios pueden funcionar de forma independiente unos de otros. Es esta propiedad la que les da el mayor valor. Esta misma propiedad permite que los servicios escalen y crezcan. No tanto en el sentido de escalar a billones de usuarios o petabytes de datos (aunque eso también puede ayudar), sino en el sentido de escalar en términos de personas a medida que los equipos y las organizaciones crecen continuamente.
Sin embargo, la independencia es un arma de doble filo. Es decir, el servicio en sí puede ejecutarse de forma fácil y natural. Pero si se implementa una función dentro de un servicio que requiere el uso de otro servicio, entonces terminamos teniendo que realizar cambios en ambos servicios casi simultáneamente. En un monolito esto es fácil de hacer, simplemente haces un cambio y lo envías a lanzamiento, pero en el caso de sincronizar servicios independientes habrá más problemas. La coordinación entre equipos y ciclos de lanzamiento destruye la agilidad.
Como parte del enfoque estándar, simplemente intentan evitar cambios molestos de un extremo a otro, dividiendo claramente la funcionalidad entre los servicios. El servicio de inicio de sesión único puede ser un buen ejemplo en este caso. Tiene un rol claramente definido que lo diferencia de otros servicios. Esta clara separación significa que en un mundo en el que las demandas de los servicios que lo rodean cambian rápidamente, es poco probable que el servicio de inicio de sesión único cambie. Existe dentro de un contexto estrictamente limitado.
El problema es que en el mundo real, los servicios empresariales no pueden mantener la misma separación pura de funciones todo el tiempo. Por ejemplo, los mismos servicios empresariales funcionan en mayor medida con datos procedentes de otros servicios similares. Si se dedica al comercio minorista en línea, procesar el flujo de pedidos, el catálogo de productos o la información del usuario se convertirá en un requisito para muchos de sus servicios. Cada uno de los servicios necesitará acceso a estos datos para funcionar.
La mayoría de los servicios empresariales comparten el mismo flujo de datos, por lo que su trabajo está invariablemente entrelazado.
Llegamos así a un punto importante del que vale la pena hablar. Si bien los servicios funcionan bien para componentes de infraestructura que operan en gran medida de forma aislada, la mayoría de los servicios empresariales terminan entrelazados mucho más estrechamente.
Dicotomía de datos
Es posible que ya existan enfoques orientados a servicios, pero aún faltan conocimientos sobre cómo compartir grandes cantidades de datos entre servicios.
El principal problema es que los datos y los servicios son inseparables. Por un lado, la encapsulación nos anima a ocultar datos para que los servicios puedan separarse entre sí y facilitar su crecimiento y cambios posteriores. Por otro lado, debemos poder dividir y conquistar libremente los datos compartidos, como cualquier otro dato. La cuestión es poder empezar a trabajar inmediatamente, con tanta libertad como en cualquier otro sistema de información.
Sin embargo, los sistemas de información tienen poco que ver con la encapsulación. De hecho, es todo lo contrario. Las bases de datos hacen todo lo posible para brindar acceso a los datos que almacenan. Vienen con una poderosa interfaz declarativa que le permite modificar los datos según sea necesario. Esta funcionalidad es importante en la etapa de investigación preliminar, pero no para gestionar la creciente complejidad de un servicio en constante evolución.
Y aquí surge un dilema. Contradicción. Dicotomía. Después de todo, los sistemas de información sirven para proporcionar datos y los servicios para ocultarlos.
Estas dos fuerzas son fundamentales. Ellos sustentan gran parte de nuestro trabajo, luchando constantemente por la excelencia en los sistemas que construimos.
A medida que los sistemas de servicios crecen y evolucionan, vemos las consecuencias de la dicotomía de los datos de muchas maneras. O la interfaz del servicio crecerá, proporcionando una gama cada vez mayor de funcionalidades y comenzará a parecerse a una base de datos propia muy sofisticada, o nos frustraremos e implementaremos alguna forma de recuperar o mover en masa conjuntos completos de datos de un servicio a otro.
A su vez, crear algo que parezca una elegante base de datos local generará una gran cantidad de problemas. No entraremos en detalles sobre por qué es peligroso. base de datos compartida, digamos simplemente que representa importantes costos operativos y de ingeniería.
Lo peor es que los volúmenes de datos magnifican los problemas de límites de servicio. Cuantos más datos compartidos haya dentro de un servicio, más compleja se volverá la interfaz y más difícil será combinar conjuntos de datos provenientes de diferentes servicios.
El enfoque alternativo de extraer y mover conjuntos de datos completos también tiene sus problemas. Un enfoque común a esta pregunta consiste en simplemente recuperar y almacenar todo el conjunto de datos y luego almacenarlo localmente en cada servicio consumidor.
El problema es que diferentes servicios interpretan de forma diferente los datos que consumen. Estos datos están siempre a mano. Se modifican y procesan localmente. Muy rápidamente dejan de tener algo en común con los datos de la fuente.
Cuanto más mutables sean las copias, más variarán los datos con el tiempo.
Para empeorar las cosas, estos datos son difíciles de corregir en retrospectiva (
Para encontrar una solución a este problema, debemos pensar de manera diferente acerca de los datos compartidos. Deben convertirse en objetos de primer nivel en las arquitecturas que construimos.
El problema es que ninguno de estos enfoques es relevante hoy en día, ya que ni las interfaces de servicio, ni la mensajería, ni la base de datos compartida ofrecen una buena solución para trabajar con datos externos. Las interfaces de servicio no son adecuadas para el intercambio de datos a cualquier escala. La mensajería mueve datos pero no almacena su historial, por lo que los datos se corrompen con el tiempo. Las bases de datos compartidas se centran demasiado en un punto, lo que frena el progreso. Inevitablemente nos quedamos atrapados en un ciclo de fallas de datos:
Ciclo de falla de datos
Streams: un enfoque descentralizado para datos y servicios
Idealmente, necesitamos cambiar la forma en que funcionan los servicios con datos compartidos. En este punto, cualquiera de los enfoques se enfrenta a la dicotomía antes mencionada, ya que no existe ningún polvo mágico que pueda rociarse sobre él para hacerlo desaparecer. Sin embargo, podemos repensar el problema y llegar a un compromiso.
Este compromiso implica un cierto grado de centralización. Podemos utilizar el mecanismo de registro distribuido porque proporciona flujos confiables y escalables. Ahora queremos que los servicios puedan unirse y operar en estos hilos compartidos, pero queremos evitar los complejos Servicios de Dios centralizados que realizan este procesamiento. Por lo tanto, la mejor opción es incorporar el procesamiento de flujo en cada servicio al consumidor. De esta manera, los servicios podrán combinar conjuntos de datos de diferentes fuentes y trabajar con ellos de la forma que necesiten.
Una forma de lograr este enfoque es mediante el uso de una plataforma de streaming. Hay muchas opciones, pero hoy veremos Kafka, ya que el uso de su Stateful Stream Processing nos permite resolver eficazmente el problema presentado.
El uso de un mecanismo de registro distribuido nos permite seguir el camino ya trillado y utilizar la mensajería para trabajar con
Si un corredor es responsable del registro distribuido en lugar de un sistema de mensajería tradicional, puede aprovechar funciones adicionales. El transporte puede escalar linealmente casi tan bien como un sistema de archivos distribuido. Los datos se pueden almacenar en registros durante bastante tiempo, por lo que no solo obtenemos intercambio de mensajes, sino también almacenamiento de información. Almacenamiento escalable sin temor a un estado compartido mutable.
Luego puede utilizar el procesamiento de flujo con estado para agregar herramientas de bases de datos declarativas a los servicios de consumo. Ésta es una idea muy importante. Si bien los datos se almacenan en flujos compartidos a los que todos los servicios pueden acceder, la agregación y el procesamiento que realiza el servicio son privados. Se encuentran aislados en un contexto estrictamente limitado.
Elimine la dicotomía de datos separando el flujo de estados inmutable. Luego agregue esta funcionalidad a cada servicio utilizando Stateful Stream Processing.
Así, si tu servicio necesita trabajar con pedidos, un catálogo de productos, un almacén, tendrá acceso completo: solo tú decidirás qué datos combinar, dónde procesarlos y cómo deben cambiar con el tiempo. A pesar de que los datos se comparten, el trabajo con ellos está completamente descentralizado. Se produce dentro de cada servicio, en un mundo donde todo va según tus reglas.
Comparta datos sin comprometer su integridad. Encapsule la función, no la fuente, en cada servicio que la necesite.
Sucede que es necesario mover datos en masa. A veces, un servicio requiere un conjunto de datos históricos local en el motor de base de datos seleccionado. El truco es que puede garantizar que, si es necesario, la copia se pueda restaurar desde la fuente accediendo al mecanismo de registro distribuido. Los conectores en Kafka hacen un gran trabajo al respecto.
Entonces, el enfoque discutido hoy tiene varias ventajas:
- Los datos se utilizan en forma de flujos comunes, que pueden almacenarse en registros durante mucho tiempo, y el mecanismo para trabajar con datos comunes está integrado en cada contexto individual, lo que permite que los servicios funcionen fácil y rápidamente. De esta manera se puede equilibrar la dicotomía de los datos.
- Los datos procedentes de diferentes servicios se pueden combinar fácilmente en conjuntos. Esto simplifica la interacción con datos compartidos y elimina la necesidad de mantener conjuntos de datos locales en la base de datos.
- Stateful Stream Processing solo almacena datos en caché y la fuente de verdad siguen siendo los registros generales, por lo que el problema de la corrupción de datos con el tiempo no es tan grave.
- En esencia, los servicios se basan en datos, lo que significa que a pesar de los volúmenes cada vez mayores de datos, los servicios aún pueden responder rápidamente a los eventos comerciales.
- Los problemas de escalabilidad recaen en el corredor, no en los servicios. Esto reduce significativamente la complejidad de escribir servicios, ya que no es necesario pensar en la escalabilidad.
- Agregar nuevos servicios no requiere cambiar los antiguos, por lo que conectar nuevos servicios resulta más fácil.
Como puede ver, esto es más que simplemente DESCANSAR. Hemos recibido un conjunto de herramientas que le permite trabajar con datos compartidos de forma descentralizada.
No todos los aspectos fueron cubiertos en el artículo de hoy. Todavía tenemos que descubrir cómo equilibrar el paradigma de solicitud-respuesta y el paradigma impulsado por eventos. Pero nos ocuparemos de esto la próxima vez. Hay temas que necesita conocer mejor, por ejemplo, por qué Stateful Stream Processing es tan bueno. Hablaremos de esto en el tercer artículo. Y hay otros constructos poderosos que podemos aprovechar si recurrimos a ellos, por ejemplo,
Pero por ahora, recuerde esto: la dicotomía de los datos es una fuerza a la que nos enfrentamos cuando creamos servicios empresariales. Y debemos recordar esto. El truco consiste en darle la vuelta a todo y empezar a tratar los datos compartidos como objetos de primera clase. Stateful Stream Processing proporciona un compromiso único para esto. Evita los “componentes de Dios” centralizados que frenan el progreso. Además, garantiza la agilidad, escalabilidad y resiliencia de los canales de transmisión de datos y los agrega a cada servicio. Por lo tanto, podemos centrarnos en la corriente general de conciencia a la que cualquier servicio puede conectarse y trabajar con sus datos. Esto hace que los servicios sean más escalables, intercambiables y autónomos. Por lo tanto, no sólo quedarán bien en pizarras blancas y pruebas de hipótesis, sino que también funcionarán y evolucionarán durante décadas.
Fuente: habr.com