Transacciones distribuidas en el contexto de la arquitectura de microservicios

Hola. Ya en septiembre, OTUS abre un set para un nuevo grupo del curso "Highload Architect" . En este sentido, continúo mi serie de publicaciones escritas específicamente para este curso, y también los invito a mi webinar gratuito, en el que les contaré en detalle sobre el programa del curso y el formato de formación en OTUS. Puede registrarse para el seminario web aquí .








Introducción



Como saben, la transición de una arquitectura monolítica a una de microservicio provoca una serie de dificultades asociadas tanto con la parte técnica del proyecto como con el factor humano. Uno de los desafíos técnicos más difíciles es garantizar la coherencia en un sistema distribuido.



Consistencia



El punto sutil es que la coherencia en el contexto de los sistemas distribuidos es diferente de la coherencia en el contexto de las bases de datos. Además, por consistencia, nos referiremos exactamente a la primera: una operación incompleta (errónea) no introduce ningún efecto y no cambia los datos; con acceso concurrente a los datos, todas las operaciones se consideran atómicas (no se puede ver el resultado intermedio de la operación) si los datos tienen varias copias (replicación) , entonces la secuencia de aplicación de operaciones en todas las copias es la misma. Es decir, de hecho, queremos recibir una transacción ACID, pero solo una distribuida.



La causa del problema



¿Por qué es difícil mantener la coherencia en una arquitectura de microservicio? El hecho es que este estilo arquitectónico a menudo implica el uso de la base de datos por patrón de servicio. Permítanme recordarles que este patrón consiste en que cada microservicio tiene su propia base o bases independientes (bases, porque además de la fuente de datos primaria, por ejemplo, se puede utilizar una caché). Este enfoque permite, por un lado, no agregar enlaces de formato de datos implícitos entre microservicios (los microservicios interactúan solo explícitamente a través de la API), por otro lado, aprovechar al máximo una ventaja de la arquitectura de microservicio como tecnología agnóstica (podemos elegir la tecnología de almacenamiento de datos que sea adecuada para la carga particular en el microservicio) ). Pero con todo esto, perdimos la garantía de coherencia de los datos. Juzga por ti mismoel monolito se comunicaba con una gran base de datos, que brindaba la capacidad de proporcionar transacciones ACID. Ahora hay muchas bases de datos, y en lugar de una gran transacción ACID, tenemos muchas pequeñas transacciones ACID. Nuestra tarea será combinar todas estas transacciones en unadistribuido .



Consistencia optimista



Lo primero que me viene a la mente es el concepto de coherencia optimista: cometemos tantas transacciones como queramos para tantos motores de almacenamiento como sea necesario. Al mismo tiempo, esperamos que todo salga bien, y si todo va mal, entonces decimos que todo saldrá bien al final. Si todo va mal al final, entonces decimos: "Sí, esto sucede, pero con una probabilidad extremadamente baja".



Dejando de lado las bromas, descuidar la coherencia cuando no es fundamental para el negocio es una buena idea, especialmente si se tiene en cuenta cuánto esfuerzo nos costará mantenerla (que espero que veas más adelante).



Opciones de coherencia



Si la coherencia es fundamental para el negocio, hay varias formas de intentar lograrlo. Si estamos hablando de una situación en la que un servicio actualiza los datos (por ejemplo, se realiza la replicación de la base de datos), entonces se pueden aplicar algoritmos de consistencia estándar como Paxos o Raft. Estas transacciones se denominan homogéneas . Si los datos son actualizados por varios servicios (es decir, se realiza una transacción heterogénea ), entonces, ¿cómo comienza la complejidad, de la que hablamos anteriormente?



Por un lado, todavía podemos evitar la necesidad de proporcionar una transacción distribuida al esforzarnos por una arquitectura basada en servicios (combinamos servicios de tal manera que la transacción sea homogénea). Tal solución no es muy canónica desde el punto de vista de los principios de la arquitectura de microservicios, pero es técnicamente mucho más simple, por lo que se usa a menudo en la práctica. Por otro lado, podemos dejar los microservicios canónicos, pero al mismo tiempo aplicar uno de los mecanismos para asegurar transacciones distribuidas: un commit en dos fases o una saga. Este artículo explorará la primera opción y discutirá la segunda la próxima vez.



Compromiso en dos fases



El mecanismo es extremadamente simple: hay algún administrador de transacciones que realmente organiza la transacción. En la primera etapa (preparación), el administrador de transacciones emite el comando apropiado para los administradores de recursos, según el cual escriben datos en sus registros que se confirmarán. Después de recibir la confirmación de todos los administradores de recursos sobre la finalización exitosa de la primera etapa, el administrador de transacciones inicia la segunda etapa y emite el siguiente comando (compromiso), según el cual los administradores de recursos aplican los cambios previamente aceptados.



A pesar de su aparente simplicidad, este enfoque tiene varias desventajas. Primero, si al menos un administrador de recursos falla en la segunda fase, se debe revertir toda la transacción. Por lo tanto, se viola uno de los principios de la arquitectura de microservicios: la resistencia a fallas (cuando llegamos a un sistema distribuido, asumimos de inmediato que la falla es la norma y no una situación excepcional). Además, si hay muchas fallas (y habrá muchas), el proceso de cancelación de transacciones deberá automatizarse (incluida la escritura de transacciones que reviertan transacciones). En segundo lugar, el administrador de transacciones en sí mismo es un punto único de falla. Debería poder emitir transaccionalmente id-shniks a las transacciones. En tercer lugar, dado que se dan comandos especiales al repositorio, es lógico suponer que el repositorio debería poder hacer esto,es decir, cumplen con el estándar XA, y no todas las tecnologías modernas lo cumplen (corredores como Kafka, RabbitMQ y soluciones NoSQL como MongoDB y Cassandra no admiten confirmaciones de dos fases).



La conclusión que se desprende de todos estos factores fue bellamente articulada por Chris Richardson: "2PC no es una opción" (el compromiso de dos fases no es una opción).



Salida



Descubrimos por qué las transacciones distribuidas son el principal problema técnico de una arquitectura de microservicio y hablamos sobre varias opciones para resolver este problema, discutimos en detalle el mecanismo de confirmación de dos fases.






Los invito a todos a inscribirse en mi seminario web sobre el curso , en el que les contaré en detalle sobre el formato de capacitación y les presentaré a todos el programa de capacitación.






Lee mas:






All Articles