Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Sugerencia
Este contenido es un extracto del libro electrónico, ".NET Microservices Architecture for Containerized .NET Applications" (Arquitectura de microservicios de .NET para aplicaciones de .NET contenedorizadas), disponible en Documentación de .NET o como un PDF descargable y gratuito que se puede leer sin conexión.
Como se ha descrito anteriormente, cuando se usa la comunicación basada en eventos, un microservicio publica un evento cuando ocurre algo notable, como cuando actualiza una entidad empresarial. Otros microservicios se suscriben a esos eventos. Cuando un microservicio recibe un evento, puede actualizar sus propias entidades empresariales, lo que podría dar lugar a que se publiquen más eventos. Esta es la esencia del concepto de coherencia final. Este sistema de publicación o suscripción normalmente se realiza mediante una implementación de un bus de eventos. El bus de eventos se puede diseñar como una interfaz con la API necesaria para suscribirse y cancelar la suscripción a eventos y publicar eventos. También puede tener una o varias implementaciones basadas en cualquier comunicación entre procesos o mensajería, como una cola de mensajería o un bus de servicio que admita la comunicación asincrónica y un modelo de publicación o suscripción.
Puede usar eventos para implementar transacciones empresariales que abarquen varios servicios, lo que proporciona coherencia final entre esos servicios. Una transacción coherente eventualmente consta de una serie de acciones distribuidas. En cada acción, el microservicio actualiza una entidad empresarial y publica un evento que desencadena la siguiente acción. Tenga en cuenta que la transacción no abarca la persistencia subyacente ni el bus de eventos, por lo que es necesario controlar la idempotencia. La figura 6-18 siguiente muestra un evento PriceUpdated publicado a través de un bus de eventos, por lo que la actualización de precios se propaga a la Cesta y a otros microservicios.
Figura 6-18. Comunicación impulsada por eventos basada en un bus de eventos.
En esta sección se describe cómo puede implementar este tipo de comunicación con .NET mediante una interfaz genérica de bus de eventos, como se muestra en la figura 6-18. Hay varias implementaciones potenciales, cada una con una tecnología o infraestructura diferente, como RabbitMQ, Azure Service Bus o cualquier otro bus de servicio comercial o de código abierto de terceros.
Uso de agentes de mensajes y buses de servicio para sistemas de producción
Como se indica en la sección de arquitectura, puede elegir entre varias tecnologías de mensajería para implementar el bus de eventos abstracto. Pero estas tecnologías están en diferentes niveles. Por ejemplo, RabbitMQ, un transporte de agentes de mensajería, está en un nivel inferior al de productos comerciales como Azure Service Bus, NServiceBus, MassTransit o Brighter. La mayoría de estos productos pueden funcionar sobre RabbitMQ o Azure Service Bus. La elección del producto depende de cuántas características y cuánta escalabilidad instantánea necesita para su aplicación.
Para implementar solo una prueba de concepto del bus de eventos para su entorno de desarrollo, como en el ejemplo eShopOnContainers, una implementación sencilla sobre RabbitMQ que se ejecuta como un contenedor podría ser suficiente. Pero para los sistemas críticos y de producción que necesitan una alta escalabilidad, es posible que quiera evaluar y usar Azure Service Bus.
Si necesita abstracciones de alto nivel y características más enriquecidas como Sagas para procesos de larga duración que facilitan el desarrollo distribuido, otros buses de servicio comerciales y de código abierto como NServiceBus, MassTransit y Brighter merecen la pena evaluar. En este caso, las abstracciones y la API que se van a utilizar suelen ser las proporcionadas directamente por los buses de servicio de alto nivel, en vez de las propias abstracciones (como las abstracciones de bus de eventos sencillos proporcionadas en eShopOnContainers). Con este propósito, puede analizar eShopOnContainers bifurcado con NServiceBus (ejemplo derivado adicional implementado por Particular Software).
Por supuesto, siempre podría crear sus propias características de Service Bus sobre tecnologías de nivel inferior como RabbitMQ y Docker, pero el trabajo necesario para "reinventar la rueda" podría ser demasiado costoso para una aplicación empresarial personalizada.
Para repetir: las abstracciones de bus de eventos de ejemplo y la implementación que se muestran en el ejemplo eShopOnContainers están diseñadas para usarse solo como prueba de concepto. Una vez que haya decidido que desea tener comunicación asincrónica y controlada por eventos, como se explica en la sección actual, debe elegir el producto de Service Bus que mejor se adapte a sus necesidades de producción.
Eventos de integración
Los eventos de integración se usan para sincronizar el estado del dominio en varios microservicios o sistemas externos. Esta funcionalidad se realiza mediante la publicación de eventos de integración fuera del microservicio. Cuando se publica un evento en varios microservicios receptores (a tantos microservicios como se suscriben al evento de integración), el controlador de eventos adecuado en cada microservicio receptor controla el evento.
Un evento de integración es básicamente una clase de almacenamiento de datos, como en el ejemplo siguiente:
public class ProductPriceChangedIntegrationEvent : IntegrationEvent
{
public int ProductId { get; private set; }
public decimal NewPrice { get; private set; }
public decimal OldPrice { get; private set; }
public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice,
decimal oldPrice)
{
ProductId = productId;
NewPrice = newPrice;
OldPrice = oldPrice;
}
}
Los eventos de integración se pueden definir en el nivel de aplicación de cada microservicio, por lo que se desacoplan de otros microservicios, de forma comparable a cómo se definen ViewModels en el servidor y el cliente. Lo que no se recomienda es compartir una biblioteca de eventos de integración común en varios microservicios; hacerlo sería acoplar esos microservicios con una sola biblioteca de datos de definición de eventos. No desea hacerlo por las mismas razones por las que no desea compartir un modelo de dominio común en varios microservicios: los microservicios deben ser completamente autónomos. Para obtener más información, consulte esta entrada de blog sobre la cantidad de datos que se van a colocar en eventos. Tenga cuidado de no llevar esto demasiado lejos, ya que en esta otra entrada de blog se describen los mensajes deficientes de datos problemáticos que pueden producir. Su diseño de los eventos debe tener como objetivo ser "justo" para las necesidades de sus consumidores.
Solo hay algunos tipos de bibliotecas que debe compartir entre microservicios. Una es las bibliotecas que son bloques de aplicación finales, como la API de cliente de Event Bus, como en eShopOnContainers. Otra es las bibliotecas que constituyen herramientas que también se pueden compartir como componentes nuGet, como serializadores JSON.
El bus de eventos
Un bus de eventos permite la comunicación estilo publicación/suscripción entre microservicios sin necesidad de que los componentes sean conscientes explícitamente entre sí, como se muestra en la figura 6-19.
Figura 6-19. Aspectos básicos de publicación/suscripción con un bus de eventos
En el diagrama anterior se muestra que el microservicio A se publica en Event Bus, que se distribuye para suscribir los microservicios B y C, sin que el publicador necesite conocer los suscriptores. El bus de eventos está relacionado con el patrón Observer y el patrón publish-subscribe.
Patrón de observador
En el patrón Observer, el objeto principal (conocido como Observable) notifica a otros objetos interesados (conocidos como observadores) con información relevante (eventos).
Patrón de publicación/suscripción (Pub/Sus)
El propósito del patrón Publish/Subscribe es el mismo que el patrón Observer: quiere notificar a otros servicios cuando se produzcan determinados eventos. Pero hay una diferencia importante entre los patrones Observer y Pub/Sub. En el patrón de observador, la difusión se realiza directamente desde el observable a los observadores, por lo que se "conocen" entre sí. Sin embargo, cuando se usa un patrón Pub/Sub, hay un tercer componente, denominado agente, agente de mensajes o bus de eventos, que el publicador y el suscriptor conocen. Por lo tanto, cuando se usa el patrón Pub/Sub, el publicador y los suscriptores se desacoplan con precisión gracias al bus de eventos o al agente de mensajes mencionado.
El middleman o el bus de eventos
¿Cómo se logra el anonimato entre el publicador y el suscriptor? Una manera fácil es dejar que un intermediario se ocupe de toda la comunicación. Un bus de eventos es uno de esos intermediarios.
Normalmente, un bus de eventos se compone de dos partes:
Abstracción o interfaz.
Una o varias implementaciones.
En la figura 6-19 puede ver cómo, desde un punto de vista de la aplicación, el bus de eventos no es más que un canal Pub/Sub. La forma de implementar esta comunicación asincrónica puede variar. Puede tener varias implementaciones para que pueda intercambiar entre ellas, en función de los requisitos del entorno (por ejemplo, entornos de producción frente a entornos de desarrollo).
En la figura 6-20, puede ver una abstracción de un bus de eventos con varias implementaciones basadas en tecnologías de mensajería de infraestructura como RabbitMQ, Azure Service Bus u otro agente de eventos o mensajes.
Figura 6- 20. Varias implementaciones de un bus de eventos
Es bueno tener definido el bus de eventos a través de una interfaz para que se pueda implementar con varias tecnologías, como RabbitMQ, Azure Service Bus u otros. Sin embargo, y como se mencionó anteriormente, el uso de sus propias abstracciones (la interfaz de bus de eventos) solo es bueno si necesita características básicas del bus de eventos compatibles con las abstracciones. Si necesita características más enriquecidas de Service Bus, probablemente debe usar la API y las abstracciones proporcionadas por su bus de servicio comercial preferido en lugar de sus propias abstracciones.
Definición de una interfaz de bus de eventos
Comencemos con algún código de implementación para la interfaz de bus de eventos y posibles implementaciones con fines de exploración. La interfaz debe ser genérica y sencilla, como en la siguiente interfaz.
public interface IEventBus
{
void Publish(IntegrationEvent @event);
void Subscribe<T, TH>()
where T : IntegrationEvent
where TH : IIntegrationEventHandler<T>;
void SubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler;
void UnsubscribeDynamic<TH>(string eventName)
where TH : IDynamicIntegrationEventHandler;
void Unsubscribe<T, TH>()
where TH : IIntegrationEventHandler<T>
where T : IntegrationEvent;
}
El Publish
método es sencillo. El bus de eventos difundirá el evento de integración pasado a cualquier microservicio, o incluso a una aplicación externa, suscrito a ese evento. El microservicio que publica el evento usa este método.
Los Subscribe
métodos (puede tener varias implementaciones en función de los argumentos) las usan los microservicios que desean recibir eventos. Este método tiene dos argumentos. La primera es el evento de integración al que suscribirse (IntegrationEvent
). El segundo argumento es el controlador de eventos de integración (o método de devolución de llamada), denominado IIntegrationEventHandler<T>
, que se ejecutará cuando el microservicio receptor obtiene ese mensaje de evento de integración.
Recursos adicionales
Algunas soluciones de mensajería listas para producción:
Azure Service Bus
https://learn.microsoft.com/azure/service-bus-messaging/NServiceBus
https://particular.net/nservicebusMassTransit
https://masstransit-project.com/