Uno de los principales problemas que he visto en un sistema con microservicios es la forma en que funcionan las transacciones cuando abarcan diferentes servicios. Dentro de nuestra propia arquitectura, hemos estado utilizando transacciones distribuidas para resolver esto, pero vienen con sus propios problemas. Especialmente los callejones sin salida han sido un dolor hasta ahora.
Otra opción parece ser algún tipo de administrador de transacciones personalizado, que conoce los flujos dentro de su sistema y se encargará de las reversiones por usted como un proceso en segundo plano que abarca todo su sistema (por lo que le dirá a los otros servicios que retrocedan). y si están caídos, notifíquelos más tarde).
¿Hay otra opción aceptada? Ambos parecen tener sus desventajas. El primero podría causar puntos muertos y un montón de otros problemas, el segundo podría resultar en inconsistencia de datos. ¿Hay mejores opciones?
fuente
Respuestas:
El enfoque habitual es aislar esos microservicios tanto como sea posible: tratarlos como unidades individuales. Luego, las transacciones se pueden desarrollar en el contexto del servicio como un todo (es decir, no como parte de las transacciones de DB habituales, aunque aún puede tener transacciones de DB internas al servicio).
Piense cómo ocurren las transacciones y qué tipo tiene sentido para sus servicios, entonces puede implementar un mecanismo de reversión que cancela la operación original, o un sistema de confirmación de 2 fases que reserva la operación original hasta que se le indique que se comprometa de verdad. Por supuesto, ambos sistemas significan que está implementando el suyo, pero ya está implementando sus microservicios.
Los servicios financieros hacen este tipo de cosas todo el tiempo: si quiero mover dinero de mi banco a su banco, no hay una transacción única como la que tendría en un DB. No sabe qué sistemas está ejecutando ninguno de los bancos, por lo que debe tratar a cada uno de manera efectiva como sus microservicios. En este caso, mi banco movería mi dinero de mi cuenta a una cuenta de cartera y luego le diría a su banco que tienen algo de dinero, si ese envío falla, mi banco reembolsará mi cuenta con el dinero que intentaron enviar.
fuente
Creo que la sabiduría estándar es nunca tener transacciones que crucen los límites del microservicio. Si cualquier conjunto de datos realmente necesita ser atómicamente consistente con otro, esas dos cosas van juntas.
Esta es una de las razones por las que es muy difícil dividir un sistema en servicios hasta que lo haya diseñado completamente. Lo que en el mundo moderno probablemente significa escrito ...
fuente
Creo que si la coherencia es un requisito importante en su aplicación, debe preguntarse si el mejor enfoque es microservicios. Como dice Martin Fowler :
Pero tal vez en su caso, puede sacrificar la consistencia en pos de disponibilidad
Sin embargo, también me pregunto si existe una estrategia para transacciones distribuidas en microservicios, pero tal vez el costo es demasiado alto. Quería darle mis dos centavos con el siempre excelente artículo de Martin Fowler y el teorema CAP .
fuente
Como se sugiere en al menos una de las respuestas aquí, pero también en otras partes de la web, es posible diseñar un microservicio que persista a las entidades juntas dentro de una transacción normal si necesita coherencia entre las dos entidades.
Pero al mismo tiempo, bien podría tener la situación en la que las entidades realmente no pertenecen al mismo microservicio, por ejemplo, registros de ventas y registros de pedidos (cuando ordena algo para completar la venta). En tales casos, es posible que necesite una forma de garantizar la coherencia entre los dos microservicios.
Las transacciones tradicionalmente distribuidas se han utilizado y, en mi experiencia, funcionan bien hasta que se escalan a un tamaño en el que el bloqueo se convierte en un problema. Puede relajar el bloqueo para que realmente solo los recursos relevantes (por ejemplo, el artículo que se vende) se "bloqueen" utilizando un cambio de estado, pero aquí es donde comienza a complicarse porque está entrando en el territorio donde necesita construir todo lógica para hacer esto, usted mismo, en lugar de tener, digamos que una base de datos lo maneja por usted.
He trabajado con compañías que han tomado la ruta de construir su propio marco de transacciones para manejar este problema complejo, pero no lo recomiendo porque es costoso y lleva tiempo madurar.
Existen productos que se pueden atornillar a su sistema que se encargan de la consistencia. Un motor de procesos de negocio es un buen ejemplo y generalmente manejan la consistencia eventualmente y mediante el uso de compensación. Otros productos funcionan de manera similar. Por lo general, termina con una capa de software cerca de los clientes, que se ocupa de la coherencia y las transacciones y llama a los (micro) servicios para realizar el procesamiento comercial real . Uno de esos productos es un conector JCA genérico que se puede usar con soluciones Java EE (por transparencia: soy el autor). Consulte http://blog.maxant.co.uk/pebble/2015/08/04/1438716480000.html para obtener más detalles y una discusión más profunda de los problemas planteados aquí.
Otra forma de manejar las transacciones y la coherencia es envolver una llamada a un microservicio en una llamada a algo transaccional como una cola de mensajes. Tome el ejemplo de registro de ventas / registro de pedidos de arriba: simplemente puede dejar que el microservicio de ventas envíe un mensaje al sistema de pedidos, que se confirma en la misma transacción que escribe la venta en la base de datos. El resultado es una solución asincrónica que escala muy bien. Utilizando tecnologías como los sockets web, incluso puede evitar el problema del bloqueo, que a menudo está relacionado con la ampliación de soluciones asincrónicas. Para obtener más ideas sobre patrones como este, consulte otro de mis artículos: http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.html .
Sea cual sea la solución que elija, es importante reconocer que solo una pequeña parte de su sistema escribirá cosas que deben ser consistentes; es probable que la mayoría del acceso sea de solo lectura. Por esa razón, cree la administración de transacciones solo en las partes relevantes del sistema, para que aún pueda escalar bien.
fuente
En microservicios hay tres formas de lograr consistencia entre diff. servicios:
Orquestación: un proceso que administra la transacción y la reversión a través de los servicios.
Coreografía: los mensajes de servicio se pasan entre sí y finalmente alcanzan un estado coherente.
Híbrido: mezcla de los dos anteriores.
Para una lectura completa, vaya al enlace: https://medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c
fuente
Hay muchas soluciones que comprometen más de lo que me siento cómodo. De acuerdo, si su caso de uso es complejo, como mover dinero entre diferentes bancos, alternativas más agradables pueden ser imposibles. Pero echemos un vistazo a lo que podemos hacer en el escenario común, donde el uso de microservicios interfiere con nuestras posibles transacciones de bases de datos.
Opción 1: evite la necesidad de transacciones si es posible
Obvio y mencionado antes, pero ideal si podemos manejarlo. ¿Los componentes pertenecían realmente al mismo microservicio? ¿O podemos rediseñar los sistemas de manera que la transacción se vuelva innecesaria? Quizás aceptar la no transaccionalidad es el sacrificio más asequible.
Opción 2: usar una cola
Si hay suficiente certeza de que el otro servicio tendrá éxito en lo que queramos que hagamos, podemos llamarlo a través de algún tipo de cola. El artículo en cola no se recogerá hasta más tarde, pero podemos asegurarnos de que esté en cola .
Por ejemplo, supongamos que queremos insertar una entidad y enviar un correo electrónico, como una sola transacción. En lugar de llamar al servidor de correo, colocamos el correo en una tabla.
Un inconveniente claro es que múltiples microservicios necesitarán acceso a la misma tabla.
Opción 3: hacer el trabajo externo al final, justo antes de completar la transacción
Este enfoque se basa en el supuesto de que es muy improbable que la transacción se confirme.
Si las consultas fallan, la llamada externa aún no se ha realizado. Si la llamada externa falla, la transacción nunca se confirma.
Este enfoque viene con las limitaciones de que solo podemos hacer una llamada externa, y debe hacerse en último lugar (es decir, no podemos usar su resultado en nuestras consultas).
Opción 4: crear cosas en estado pendiente
Como se publicó aquí , podemos hacer que múltiples microservicios creen diferentes componentes, cada uno en un estado pendiente, no transaccional.
Se realiza cualquier validación, pero nada se crea en un estado definitivo. Después de que todo se haya creado con éxito, cada componente se activa. Por lo general, esta operación es tan simple y las probabilidades de que algo salga mal son tan pequeñas que incluso podemos preferir hacer la activación de manera no transaccional.
El mayor inconveniente es probablemente que tenemos que tener en cuenta la existencia de elementos pendientes. Cualquier consulta de selección debe considerar si incluir datos pendientes. La mayoría debería ignorarlo. Y las actualizaciones son otra historia por completo.
Opción 5: dejar que el microservicio comparta su consulta
¿Ninguna de las otras opciones lo hace por ti? Entonces seamos poco ortodoxos .
Dependiendo de la compañía, esta puede ser inaceptable. Soy consciente. Esto no es ortodoxo. Si no es aceptable, toma otra ruta. Pero si esto se ajusta a su situación, resuelve el problema de manera simple y poderosa. Podría ser el compromiso más aceptable.
Hay una manera de convertir las consultas de múltiples microservicios en una transacción de base de datos simple y única.
Devuelve la consulta, en lugar de ejecutarla.
En cuanto a la red, cada microservicio debe poder acceder a cada base de datos. Tenga esto en cuenta, también con respecto a la escala futura.
Si las bases de datos involucradas en la transacción están en el mismo servidor, esta será una transacción regular. Si están en servidores diferentes, será una transacción distribuida. El código es el mismo independientemente.
Recibimos la consulta, incluido su tipo de conexión, sus parámetros y su cadena de conexión. Podemos envolverlo en una clase de comando ejecutable ordenada, manteniendo el flujo legible: la llamada al microservicio da como resultado un comando, que ejecutamos, como parte de nuestra transacción.
La cadena de conexión es lo que nos proporciona el microservicio de origen, por lo que, para todos los efectos, el microservicio todavía considera que la consulta se ejecuta. Simplemente lo estamos enrutando físicamente a través del microservicio del cliente. Eso hace una diferencia? Bueno, nos permite ponerlo en la misma transacción con otra consulta.
Si el compromiso es aceptable, este enfoque nos brinda la transaccionalidad directa de una aplicación monolítica, en una arquitectura de microservicio.
fuente
Comenzaría con la descomposición del espacio problemático, identificando los límites de su servicio . Cuando se hace correctamente, nunca necesitaría tener transacciones entre servicios.
Los diferentes servicios tienen sus propios datos, comportamiento, fuerzas motivadoras, gobierno, reglas comerciales, etc. Un buen comienzo es enumerar qué capacidades de alto nivel tiene su empresa. Por ejemplo, marketing, ventas, contabilidad, soporte. Otro punto de partida es la estructura organizativa, pero tenga en cuenta que existe una advertencia: por alguna razón (política, por ejemplo), podría no ser el esquema óptimo de descomposición empresarial. Un enfoque más estricto es el análisis de la cadena de valor . Recuerde, sus servicios también pueden incluir personas, no es estrictamente software. Los servicios deben comunicarse entre sí a través de eventos .
El siguiente paso es tallar estos servicios. Como resultado, aún obtienes agregados relativamente independientes . Representan una unidad de consistencia. En otras palabras, sus componentes internos deben ser consistentes y ACID. Los agregados se comunican entre sí a través de eventos.
Si cree que su dominio exige coherencia primero, piense de nuevo. Ninguno de los sistemas grandes y de misión crítica está construido con esto en mente. Todos se distribuyen y eventualmente son consistentes. Mira el clásico artículo de Pat Helland .
Aquí hay algunos consejos prácticos sobre cómo construir un sistema distribuido.
fuente