Patrón Choreography

Azure Event Grid
Azure Service Bus

Haga que cada componente del sistema participe en el proceso de toma de decisiones sobre el flujo de trabajo de una transacción empresarial, en lugar de depender de un punto central de control.

Contexto y problema

En la arquitectura de microservicios, se suele dar el caso de que una aplicación basada en la nube se divide en varios servicios pequeños que trabajan conjuntamente para procesar una transacción empresarial de un extremo a otro. Para reducir el acoplamiento entre servicios, cada servicio es responsable de una única operación empresarial. Algunas ventajas incluyen el desarrollo más rápido, la base de código más reducida y la escalabilidad. Sin embargo, el diseño de un flujo de trabajo eficaz y escalable es un desafío y a menudo requiere una comunicación entre servicios compleja.

Los servicios se comunican entre sí mediante API bien definidas. Incluso una única operación empresarial puede dar lugar a varias llamadas punto a punto entre todos los servicios. Un patrón común para la comunicación es usar un servicio centralizado que actúe como el orquestador. Confirma todas las solicitudes entrantes y delega las operaciones en los servicios respectivos. Al hacerlo, también administra el flujo de trabajo de toda la transacción empresarial. Cada servicio completa una operación y no es consciente del flujo de trabajo global.

El patrón de orquestador reduce la comunicación punto a punto entre los servicios, pero tiene algunos inconvenientes debido al estrecho acoplamiento entre el orquestador y otros servicios que participan en el procesamiento de la transacción empresarial. Para ejecutar tareas en una secuencia, el orquestador necesita tener conocimientos de dominio sobre las responsabilidades de esos servicios. Si quiere agregar o quitar servicios, la lógica existente se interrumpirá y deberá reorganizar partes de la ruta de comunicación. Aunque puede configurar el flujo de trabajo y agregar o quitar servicios fácilmente con un orquestador bien diseñado, este tipo de implementación es complejo y difícil de mantener.

Procesamiento de una solicitud con un orquestador central

Solución

Permita a cada servicio decidir cuándo y cómo se procesa una operación empresarial, en lugar de depender de un orquestador central.

Una manera de implementar organización es usar el patrón de mensajería asincrónica para coordinar las operaciones empresariales.

Procesamiento de una solicitud mediante un organizador

Una solicitud de cliente publica mensajes en una cola de mensajes. A medida que llegan los mensajes, se insertan en los suscriptores o servicios interesados en dicho mensaje. Cada servicio suscrito realiza su operación como se indica en el mensaje y responde a la cola de mensajes con éxito o error de la operación. En caso de éxito, el servicio puede volver a insertar un mensaje en la misma cola o en una diferente para que otro servicio pueda continuar con el flujo de trabajo si es necesario. Si se produce un error en una operación, el bus de mensajes puede reintentar la operación.

De esta forma, los servicios organizan el flujo de trabajo entre sí sin depender de un orquestador ni tener comunicación directa entre ellos.

Dado que no hay comunicación punto a punto, este patrón ayuda a reducir el acoplamiento entre los servicios. Además, puede quitar el cuello de botella de rendimiento causado por el orquestador cuando tiene que tratar todas las transacciones.

Cuándo usar este patrón

Use el patrón de coreografía si espera actualizar o reemplazar los servicios con frecuencia, y agregue o quite algunos, finalmente. Se puede modificar toda la aplicación con menos esfuerzo y con una interrupción mínima en los servicios existentes.

Tenga en cuenta este patrón si experimenta cuellos de botella de rendimiento en el orquestador central.

Este patrón es un modelo natural para la arquitectura sin servidor donde todos los servicios pueden ser de corta duración o basados en eventos. Los servicios se pueden poner en marcha debido a un evento, realizan su tarea y se quitan cuando esta finaliza.

Problemas y consideraciones

La descentralización del orquestador puede producir problemas mientras administra el flujo de trabajo.

Si un servicio no puede completar una operación empresarial, puede ser difícil recuperarse de ese error. Se puede hacer que el servicio indique el error mediante la activación de un evento. Otro servicio se suscribe a esos eventos con errores y toma las medidas necesarias, como aplicar transacciones de compensación para deshacer las operaciones correctas en una solicitud. El servicio con errores también podría no desencadenar un evento para el error. En ese caso, considere la posibilidad de usar un mecanismo de reintento o de tiempo de espera para reconocer esa operación como un error. Para ver un ejemplo, consulte la sección Ejemplo.

Es fácil implementar un flujo de trabajo cuando se quieren procesar operaciones comerciales independientes en paralelo. Puede usar un solo bus de mensajes. Sin embargo, el flujo de trabajo puede ser complicado cuando la organización debe aparecer en una secuencia. Por ejemplo, el servicio C puede iniciar su operación solo después de que el servicio A y el servicio B hayan completado sus operaciones correctamente. Un enfoque consiste en tener varios buses o colas de mensajes que reciban mensajes en el orden requerido. Para obtener más información, vea la sección Ejemplo.

El patrón de organización se convierte en un desafío si el número de servicios aumenta rápidamente. Dado el gran número de elementos móviles independientes, el flujo de trabajo entre servicios tiende a ser complejo. Además, el seguimiento distribuido se vuelve difícil.

El orquestador administra de forma centralizada la resistencia del flujo de trabajo y puede convertirse en un único punto de error. Por otro lado, para la organización, el rol se distribuye entre todos los servicios y la resistencia se vuelve menos estable.

Cada servicio no es solo responsable de la resistencia de su funcionamiento, sino también del flujo de trabajo. Esta responsabilidad puede ser pesada para el servicio y difícil de implementar. Cada servicio debe reintentar los errores transitorios, no transitorios y de tiempo de espera, de modo que la solicitud finalice correctamente, si es necesario. Además, el servicio debe ser diligente con respecto a la comunicación del éxito o fracaso de la operación para que otros servicios puedan actuar en consecuencia.

Diseño de cargas de trabajo

Un arquitecto debe evaluar cómo se puede usar el patrón Choreography en el diseño de su carga de trabajo para abordar los objetivos y principios descritos en los pilares del Marco de buena arquitectura de Azure. Por ejemplo:

Fundamento Cómo apoya este patrón los objetivos de los pilares
La excelencia operativa ayuda a ofrecer calidad de carga de trabajo a través de procesos estandarizados y cohesión de equipos. Dado que los componentes distribuidos de este patrón son autónomos y están diseñados para poderlos reemplazar, puede modificar la carga de trabajo con menos cambios generales en el sistema.

- OE:04 Herramientas y procesos
La eficiencia del rendimiento ayuda a que la carga de trabajo satisfaga eficazmente las demandas mediante optimizaciones en el escalado, los datos y el código. Este patrón proporciona una alternativa cuando se producen cuellos de botella de rendimiento en una topología de orquestación centralizada.

- PE:02 Planeamiento de capacidad
- PE:05 Escapado y particiones

Al igual que con cualquier decisión de diseño, hay que tener en cuenta las ventajas y desventajas con respecto a los objetivos de los otros pilares que podrían introducirse con este patrón.

Ejemplo

En este ejemplo se muestra el patrón de coreografía mediante la creación de funciones de ejecución de cargas de trabajo nativas de la nube controladas por eventos junto con microservicios. Cuando un cliente solicita que se envíe un paquete, la carga de trabajo asigna un dron. Una vez que el paquete está listo para ser recogido por el dron programado, se inicia el proceso de entrega. Mientras está en tránsito, la carga de trabajo gestiona la entrega hasta que adquiere el estado de enviado.

Este ejemplo es una refactorización de la implementación de Drone Delivery que reemplaza el patrón Orchestrator por el patrón Choreography.

Diagrama de una carga de trabajo nativa de nube controlada por eventos que implementa el patrón de coreografía

El servicio de ingesta controla las solicitudes del cliente y las convierte en mensajes, incluidos los detalles de entrega. Las transacciones empresariales se inician después de consumir esos nuevos mensajes.

Una transacción empresarial de un solo cliente requiere tres operaciones comerciales distintas: crear o actualizar un paquete, asignar un dron para entregar el paquete y el control adecuado de la entrega que consiste en verificar y, finalmente, crear conciencia cuando se envía. Tres microservicios desempeñan el procesamiento empresarial: Servicios de empaquetado, Scheduler de drones y entrega. En lugar de un orquestador central, los servicios usan mensajería para comunicarse entre ellos. Cada servicio sería responsable de implementar un protocolo de antemano que coordine de forma descentralizada el flujo de trabajo empresarial.

Diseño

La transacción empresarial se procesa en una secuencia a través de varios saltos. Cada salto comparte un bus de un solo mensaje entre todos los servicios empresariales.

Cuando un cliente envía una solicitud de entrega a través de un punto de conexión HTTP, el servicio de ingesta lo recibe, convierte la solicitud en un mensaje y, a continuación, publica el mensaje en el bus de mensajes compartido. Los servicios empresariales suscritos van a consumir nuevos mensajes añadidos al bus. Al recibir el mensaje, los servicios empresariales pueden completar la operación correcta o incorrectamente o la solicitud puede agotar el tiempo de espera. Si se realiza correctamente, el servicio responde al bus con el código de estado correcto, genera un nuevo mensaje de operación y lo envía al bus de mensajes. Si se produce un error o se agota el tiempo de espera, el servicio notifica el error enviando el código de motivo al bus de mensajes. Además, el mensaje se enviará a mensajes fallidos para su posterior control. También se cambia el DLQ de los mensajes que no se pudieron recibir o procesar dentro de un período de tiempo razonable y adecuado.

El diseño utiliza varios buses de mensajes para procesar toda la transacción empresarial. Microsoft Azure Service Bus y Microsoft Azure Event Grid se crean para proporcionar la plataforma del servicio de mensajería para este diseño. La carga de trabajo se implementa en Azure Container Apps que hospeda Azure Functions para la ingesta y las aplicaciones que controlan el procesamiento controlado por eventos que ejecuta la lógica empresarial.

El diseño garantiza que la coreografía se produzca en una secuencia. Un solo espacio de nombres de Azure Service Bus contiene un tema con dos suscripciones y una cola compatible con la sesión. El servicio de ingesta publica mensajes en el tema. El servicio de paquetes y el servicio Drone Scheduler se suscriben al tema y publican mensajes que comunican el éxito a la cola. Incluir un identificador de sesión común con un GUID que se asocie al identificador de entrega permite el control ordenado de secuencias sin enlazar de mensajes relacionados. El servicio de entrega espera dos mensajes relacionados por transacción. El primer mensaje indica que el paquete está listo para enviarse y el segundo indica que se programa un dron.

Este diseño usa Azure Service Bus para controlar mensajes de alto valor que no se pueden perder ni duplicar durante todo el proceso de entrega. Cuando se envía el paquete, también se publica un cambio de estado en Azure Event Grid. En este diseño, el emisor del evento no tiene ninguna expectativa sobre cómo se controla el cambio de estado. Los servicios de organización de bajada que no se incluyen como parte de este diseño podrían estar escuchando este tipo de evento y reaccionar ejecutando lógica de propósito empresarial específica (es decir, enviar por correo electrónico el estado del pedido enviado al usuario).

Si planea implementarlo en otro servicio de proceso, como AKS pub-sub pattern application boilplate, podría implementarse con dos contenedores en el mismo pod. Un contenedor ejecuta el embajador que interactúa con el bus de mensajes de preferencia mientras que el otro ejecuta la lógica empresarial. El enfoque con dos contenedores en el mismo pod mejora el rendimiento y la escalabilidad. El embajador y el servicio empresarial comparten la misma red, lo que permite una baja latencia y un alto rendimiento.

Para evitar las operaciones de reintento en cascada que pueden dar lugar a varios esfuerzos, los servicios empresariales deben marcar inmediatamente los mensajes inaceptables. Es posible enriquecer los mensajes mediante códigos de motivo conocidos o un código de aplicación definido, por lo que se puede mover a una cola de mensajes fallidos (DLQ). Considere la posibilidad de administrar problemas de coherencia que implementan Saga desde servicios de bajada. Por ejemplo, otro servicio podría controlar los mensajes fallidos con fines de corrección solo mediante la ejecución de una transacción dinámica, de reintento o de compensación.

Los servicios empresariales son idempotentes para asegurarse de que las operaciones de reintento no producen recursos duplicados. Por ejemplo, el servicio de empaquetado utiliza operaciones upsert para agregar datos al almacén de datos.

Tenga en cuenta estos patrones en el diseño de la organización.