¿Estás seguro de que utilizas microservicios?

Hola Habr!

La práctica muestra que con la continua relevancia del paradigma del microservicio, no faltan interpretaciones , críticas e incluso desacreditaciones . Por lo tanto, volviendo a nuestras publicaciones traducidas, decidimos hablar específicamente sobre microservicios , o más bien, considerar una respuesta detallada a la pregunta planteada en el título del artículo.

El término "microservicios" apareció por primera vez alrededor de 2011/2012. Desde entonces, sin microservicios en el entorno de TI, casi en ninguna parte. Sin embargo, después de tantos años, ¿hemos logrado entender adecuadamente qué son los microservicios?

Primero, veamos algunas definiciones:

Microservicios es un enfoque para el desarrollo de software (...), según el cual una aplicación está estructurada como un conjunto de servicios poco acoplados . En una arquitectura de microservicio, los servicios son muy detallados y los protocolos utilizados son livianos . El beneficio de descomponer una aplicación en servicios individuales más pequeños es mejorar la modularidad . De esta forma, la aplicación es más fácil de comprender, desarrollar, probar y la aplicación misma se vuelve más resistente a la erosión de la arquitectura. Con este enfoque de la arquitectura, el desarrollo es paralelo y los pequeños equipos autónomos tienen la oportunidad de desarrollar , implementar y escalar de forma independiente los servicios de los que son responsables. También con este enfoque, la refactorización continua se puede aplicar a la arquitectura de cada servicio. Las arquitecturas de microservicios también están diseñadas para la entrega continua y la implementación continua .
en.wikipedia.org/wiki/Microservices

Aquí hay otro pasaje de Martin Fowler:

El término "arquitectura de microservicios" se ha arraigado firmemente en los últimos años para describir una forma particular de diseñar tales aplicaciones, cada una de las cuales es un conjunto de servicios implementados de forma independiente . Aunque no existe una definición exacta de este estilo arquitectónico, hay una serie de características generales con respecto a la organización de la aplicación en torno a sus capacidades comerciales, implementación automatizada, recopilación de información en terminales y gestión descentralizada de idiomas y datos .
martinfowler.com/articles/microservices.html

Probablemente haya notado que la similitud principal entre estas dos formulaciones es la independencia de los servicios y las opciones. ¡Ahora intentemos responder algunas preguntas e intentar averiguar si el sistema que está implementando realmente tiene una base de microservicio!

¿Su sistema le permite reiniciar los servicios de forma independiente?

¿O necesita reiniciar los servicios en un orden determinado? Este comportamiento puede ser dictado por sistemas de clúster específicos en los que el nodo se conecta al nodo semilla, por ejemplo, en el clúster de Akka, si no se maneja correctamente. Otros casos pueden estar asociados con conexiones de larga duración o enchufes que solo se pueden abrir en un lado. Recuerde que la comunicación debe seguir siendo simple y ligera. Aún mejor si es completamente asíncrono y está basado en mensajes. Todas las restricciones que hacen que los servicios dependan unos de otros a largo plazo pueden convertirse en dificultades con el soporte. Los problemas con uno de los servicios pueden causar problemas en todos los demás servicios que dependen de él, lo que algún día puede resultar en una falla global grave.

¿Se pueden implementar servicios de forma independiente?

Hay casos en que la liberación y la implementación de todos los servicios en el sistema deben ocurrir simultáneamente. Este proceso lleva varios días. Por supuesto, en este caso, las posibilidades de introducir un despliegue continuo son limitadas, se producen demoras y la capacidad de respuesta empeora. Lleva demasiado tiempo lidiar con errores urgentes, simplemente debido a los costos asociados con el proceso de implementación. Cuando diferentes equipos están ocupados apoyando diferentes servicios, cualquier dependencia de implementación puede evitar que otros equipos implementen sus servicios, lo que hará que se suspenda el trabajo y disminuya su efectividad.

¿Puede una modificación implicar la necesidad de realizar modificaciones sincronizadas a otros servicios?

Uno puede imaginar las muchas razones que conducen a este comportamiento. En primer lugar, estos son cambios de API. Sucede que es suficiente agregar un nuevo campo a la solicitud de API para que la compatibilidad entre los servicios se interrumpa. Sin embargo, existen soluciones alternativas para ayudar a lidiar con estas situaciones:

  • Quizás desee mantener la compatibilidad con versiones anteriores obligatorias para todos los cambios realizados en la API; es decir, cada vez que se realiza un cambio fundamental, se agrega una nueva versión de la API. Esto no significa que siempre deba admitir muchas versiones de la API; solo los necesitará durante el período hasta que se complete la migración. Simplemente puede hablar con otros equipos y asegurarse de que todos hayan completado la migración, o activar las métricas para juzgar si todavía se está utilizando una API específica.
  • También puede considerar esta opción: implemente varias versiones de su aplicación a la vez, permitiendo que otros servicios cambien a la última versión y luego desactive la anterior.
  • También intente usar formatos que admitan la evolución de los circuitos, por ejemplo, Avro , donde al agregar un nuevo campo de compatibilidad no se puede violar. Cuando falta un valor para un campo, se puede usar el valor predeterminado.

Otra razón para la implementación sincronizada es el uso de código compartido por diferentes servicios. Dependiendo de cómo se construya el proceso de lanzamiento en la parte compartida del código, cualquier cambio puede requerir actualización en todos los servicios.

¿Un cambio en la base de datos causa un cambio en muchos servicios?

Sí, la comunicación a través de una base de datos es un antipatrón bien conocido. En esta situación, es necesario actualizar el esquema y cambiar simultáneamente el código de varios microservicios a la vez. Es mejor asegurarse de que solo un servicio sea responsable de administrar el circuito. En este caso, resulta mucho más fácil mantener la base de datos (es decir, realizar sus actualizaciones) e implementar versiones. Si ya se encuentra en una situación con el uso compartido de la base de datos, intente limitar el número de operaciones de escritura para que otro servicio funcione solo para el consumo.

¿Se puede actualizar una dependencia o versión de Java en un solo servicio?

En teoría, siempre debería ser posible, ¿verdad? Pero realmente no. Hay muchos ejemplos de bibliotecas "compartidas" o simplemente proyectos que contienen definiciones de todas las dependencias utilizadas en el sistema. El objetivo del paradigma de los microservicios es garantizar que cada uno de ellos sea una entidad independiente, y que los diferentes microservicios ni siquiera tengan que escribirse en la misma pila tecnológica. Al comenzar un nuevo proyecto, debe poder decidir por sí mismo qué tecnología es mejor para él. Cualquier enlace causará nuevos problemas. En algún momento, es posible que deba actualizar a Scala o JDK, pero, por supuesto, no desea hacerlo de inmediato en todo el sistema. En un sistema donde se observa correctamente el nivel de detalle, puede actualizar un servicio individual, implementarlo, probarlo y monitorearlo durante varios días o semanas, y luego, si todo está correcto, puede realizar actualizaciones en el resto del sistema. Sin embargo, cuando existen mecanismos de unificación common u otros mecanismos de unificación fuertes, tales posibilidades son muy limitadas.

¿Su marco o biblioteca elegida lo obliga a detenerse en la misma opción en otros servicios?

Algunas bibliotecas son bastante invasivas. Cuando decide integrarlos con sistemas externos, puede resultar repentinamente que su elección no es tan buena cuando va a implementar un nuevo servicio en una pila tecnológica diferente. Los costos asociados con su integración en el mismo espíritu que el resto de los servicios pueden ser simplemente insoportables.

Supongamos, por ejemplo, que tiene varios servicios escritos en NodeJS que usan activamente JSON: API . Luego, desea implementar un nuevo servicio en Scala y parece que no hay una biblioteca cliente adecuada (hace unos años tuvimos un caso similar, pero ahora la situación en esta área ha mejorado ligeramente).

¿La falla de un solo servicio causa una falla de todo el sistema?

Suponga que la comunicación en su sistema es completamente sincrónica. Un servicio hace una solicitud a otro y espera una respuesta de él. Si el primer servicio falla, entonces el segundo servicio no podrá funcionar en absoluto. Si esta es la situación en su sistema, intente implementar en él una comunicación asincrónica basada en mensajes. Puede ser un poco más difícil simular todos los procesos, pero este enfoque permite suavizar los efectos de las fallas.

En los casos en que esto no sea posible, puede intentar limitar temporalmente el conjunto de características de la aplicación. Por ejemplo, en la página de la tienda en línea se muestra información sobre el producto y se revisan, además, la información y las revisiones del producto provienen de dos fuentes diferentes. Si el microservicio de revisiones no funciona, aún puede mostrar la página del producto simplemente informando al usuario que el sistema de revisión no está disponible temporalmente.

¿Hay algún componente compartido en su sistema?

¿Tienes código compartido? Configuración? Cualquier cosa? Sí, esta es una pregunta no trivial. ¡Los defensores de la pureza de los microservicios creen que es mejor no permitir entidades compartidas en el sistema! Es mejor simplemente copiar que dividir el código. En la práctica, a veces se considera aceptable tener varias bibliotecas compartidas , pero con limitaciones estrictas. Quizás sean bibliotecas que contengan archivos proto para memorias intermedias de protocolo o simplemente objetos POJO ordinarios. Mejor evitar dependencias en tales bibliotecas. Tuvimos un caso en el que una biblioteca cliente simple que causaba conflictos de dependencia en un sistema grande. En algún momento, comenzó a parecer que los elementos más antiguos de la aplicación usaban la antigua biblioteca HTTP, que no podía actualizarse. Afortunadamente, estas situaciones se pueden evitar mediante el cambio de nombre de dependencias en el proceso de compilación (sombreado de dependencias), pero tenga mucho cuidado.

El daño causado por la sobrecohesión de los servicios es mucho más perjudicial por los problemas causados ​​por la repetibilidad del código.

Sam Newman, Creación de microservicios

Conclusiones

Implementar el sistema perfecto basado en microservicios no es una tarea fácil. Las personas tienden a "cortar esquinas", introduciendo vínculos estrechos en el sistema que surgen debido al código compartido, y en algún momento dicho sistema en la práctica se convierte en un monolito distribuido. A menudo, esto sucede si los microservicios comienzan a usarse ya al comienzo del proyecto, cuando el área temática aún no está bien estudiada y no tiene un fondo sólido y listo en DevOps. Es mejor comenzar con un monolito adecuadamente estructurado , donde todas las áreas de responsabilidad sean completamente conocidas y puedan separarse fácilmente entre sí. Sin embargo, desde el principio, debe asegurarse de que su sistema esté diseñado de manera limpia y que la organización del paquete se siga con cuidado, lo que posteriormente le proporcionará una migración de código fácil a nuevos servicios (por ejemplo, un solo paquete de "nivel superior" puede ser la base para un nuevo microservicio) . ArchUnit puede ayudar con el análisis de dependencias que surgen entre paquetes.

(...) no debe comenzar inmediatamente a trabajar en un nuevo proyecto con la introducción de microservicios, incluso si está seguro de que su aplicación será lo suficientemente grande como para que los microservicios se justifiquen.
martinfowler.com/bliki/MonolithFirst.html

¡Recuerde, los microservicios deberían simplificar tanto el desarrollo como el soporte! Todo lo que necesita son servicios poco acoplados, cada uno de los cuales puede implementarse independientemente de los demás.

Source: https://habr.com/ru/post/478590/


All Articles