Op gebeurtenissen gebaseerde communicatie tussen microservices (integratiegebeurtenissen) implementeren

Tip

Deze inhoud is een fragment uit het eBook, .NET Microservices Architecture for Containerized .NET Applications, beschikbaar op .NET Docs of als een gratis downloadbare PDF die offline kan worden gelezen.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

Zoals eerder beschreven, publiceert een microservice, wanneer u communicatie op basis van gebeurtenissen gebruikt, een gebeurtenis wanneer er iets opmerkelijks gebeurt, bijvoorbeeld wanneer een bedrijfsentiteit wordt bijgewerkt. Andere microservices abonneren zich op deze gebeurtenissen. Wanneer een microservice een gebeurtenis ontvangt, kan deze zijn eigen bedrijfsentiteiten bijwerken, wat kan leiden tot meer gebeurtenissen die worden gepubliceerd. Dit is de essentie van het uiteindelijke consistentieconcept. Dit publicatie-/abonneersysteem wordt meestal uitgevoerd met behulp van een implementatie van een gebeurtenisbus. De gebeurtenisbus kan worden ontworpen als een interface met de API die nodig is om zich te abonneren op gebeurtenissen en om gebeurtenissen te publiceren. Het kan ook een of meer implementaties hebben op basis van elke communicatie tussen processen of berichten, zoals een berichtenwachtrij of een servicebus die asynchrone communicatie ondersteunt en een model voor publiceren/abonneren.

U kunt gebeurtenissen gebruiken om zakelijke transacties te implementeren die meerdere services omvatten, waardoor u uiteindelijk consistentie krijgt tussen deze services. Een uiteindelijk consistente transactie bestaat uit een reeks gedistribueerde acties. Bij elke actie werkt de microservice een bedrijfsentiteit bij en publiceert een gebeurtenis die de volgende actie activeert. Houd er rekening mee dat de transactie niet de onderliggende persistentie en gebeurtenisbus omvat, dus idempotentie moet worden verwerkt. In afbeelding 6-18 hieronder ziet u een PriceUpdated-gebeurtenis die is gepubliceerd via een gebeurtenisbus, zodat de prijsupdate wordt doorgegeven aan de Basket en andere microservices.

Diagram of asynchronous event-driven communication with an event bus.

Afbeelding 6-18. Gebeurtenisgestuurde communicatie op basis van een gebeurtenisbus

In deze sectie wordt beschreven hoe u dit type communicatie met .NET kunt implementeren met behulp van een algemene Event Bus-interface, zoals wordt weergegeven in afbeelding 6-18. Er zijn meerdere mogelijke implementaties, elk met behulp van een andere technologie of infrastructuur, zoals RabbitMQ, Azure Service Bus of een andere opensource- of commerciële servicebus van derden.

Berichtenbrokers en servicebussen gebruiken voor productiesystemen

Zoals vermeld in de architectuursectie, kunt u kiezen uit meerdere berichtentechnologieën voor het implementeren van uw abstracte gebeurtenisbus. Maar deze technologieën zijn op verschillende niveaus. RabbitMQ, een berichtenbrokertransport, bevindt zich bijvoorbeeld op een lager niveau dan commerciële producten zoals Azure Service Bus, NServiceBus, MassTransit of Brighter. De meeste van deze producten kunnen bovenop RabbitMQ of Azure Service Bus werken. Uw keuze voor het product is afhankelijk van het aantal functies en hoeveel kant-en-klare schaalbaarheid u nodig hebt voor uw toepassing.

Voor het implementeren van alleen een proof-of-concept voor een event bus voor uw ontwikkelomgeving, zoals in het voorbeeld eShopOnContainers, is een eenvoudige implementatie bovenop RabbitMQ die als een container wordt uitgevoerd, mogelijk voldoende. Maar voor bedrijfskritieke en productiesystemen die een hoge schaalbaarheid nodig hebben, kunt u Azure Service Bus evalueren en gebruiken.

Als u abstracties op hoog niveau en rijkere functies zoals Sagas nodig hebt voor langlopende processen die gedistribueerde ontwikkeling eenvoudiger maken, zijn andere commerciële en opensource servicebussen zoals NServiceBus, MassTransit en Brighter de moeite waard te evalueren. In dit geval zijn de abstracties en API die moeten worden gebruikt meestal rechtstreeks de abstracties die worden geleverd door die servicebussen op hoog niveau in plaats van uw eigen abstracties (zoals de eenvoudige gebeurtenisbusabstracties die worden geleverd bij eShopOnContainers). Hiervoor kunt u de forked eShopOnContainers onderzoeken met behulp van NServiceBus (aanvullend afgeleid voorbeeld geïmplementeerd door Specifieke Software).

Natuurlijk kunt u altijd uw eigen servicebusfuncties bouwen op basis van technologieën op lager niveau, zoals RabbitMQ en Docker, maar het werk dat nodig is om het wiel opnieuw uit te vinden, kan te kostbaar zijn voor een aangepaste bedrijfstoepassing.

Ter herhaling: de voorbeeldgebeurtenisbusabstracties en -implementatie die in het voorbeeld eShopOnContainers worden getoond, zijn bedoeld om alleen te worden gebruikt als bewijs van concept. Zodra u hebt besloten dat u asynchrone en gebeurtenisgestuurde communicatie wilt hebben, zoals wordt uitgelegd in de huidige sectie, moet u het service bus-product kiezen dat het beste past bij uw behoeften voor productie.

Integratiegebeurtenissen

Integratie-gebeurtenissen worden gebruikt voor het synchroniseren van de domeinstatus in meerdere microservices of externe systemen. Deze functionaliteit wordt uitgevoerd door integratie-gebeurtenissen buiten de microservice te publiceren. Wanneer een gebeurtenis wordt gepubliceerd naar meerdere ontvangende microservices (voor zoveel microservices als die zijn geabonneerd op de integratiegebeurtenis), verwerkt de juiste gebeurtenis-handler in elke ontvanger microservice de gebeurtenis.

Een integratiegebeurtenis is in feite een klasse voor gegevensopslag, zoals in het volgende voorbeeld:

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;
    }
}

De integratiegebeurtenissen kunnen worden gedefinieerd op toepassingsniveau van elke microservice, zodat ze worden losgekoppeld van andere microservices, op een manier die vergelijkbaar is met de manier waarop ViewModels worden gedefinieerd op de server en client. Wat niet wordt aanbevolen, is het delen van een algemene bibliotheek met integratie-gebeurtenissen in meerdere microservices; Als u dat doet, koppelt u deze microservices aan één gegevensbibliotheek voor gebeurtenisdefinities. U wilt dit niet doen om dezelfde redenen dat u geen gemeenschappelijk domeinmodel wilt delen in meerdere microservices: microservices moeten volledig autonoom zijn. Zie dit blogbericht voor meer informatie over de hoeveelheid gegevens die in gebeurtenissen moet worden geplaatst. Wees voorzichtig om dit niet te ver te nemen, omdat in dit andere blogbericht de probleemgegevens worden beschreven die onvoldoende berichten kunnen produceren. Uw ontwerp van uw evenementen moet erop gericht zijn om 'precies geschikt' te zijn voor de behoeften van hun consumenten.

Er zijn slechts enkele soorten bibliotheken die u moet delen tussen microservices. Een daarvan zijn bibliotheken die definitieve toepassingsblokken zijn, zoals de Event Bus-client-API, zoals in eShopOnContainers. Een andere is bibliotheken die hulpprogramma's vormen die ook kunnen worden gedeeld als NuGet-onderdelen, zoals JSON-serializers.

De gebeurtenisbus

Met een gebeurtenisbus kan communicatie tussen microservices worden gepubliceerd/geabonneerd zonder dat de onderdelen expliciet van elkaar op de hoogte moeten zijn, zoals wordt weergegeven in afbeelding 6-19.

A diagram showing the basic publish/subscribe pattern.

Afbeelding 6-19. Basisbeginselen publiceren/abonneren met een gebeurtenisbus

In het bovenstaande diagram ziet u dat microservice A publiceert naar Event Bus, dat distribueert naar het abonneren van microservices B en C, zonder dat de uitgever de abonnees hoeft te kennen. De gebeurtenisbus is gerelateerd aan het waarnemerspatroon en het patroon publish-subscribe.

Waarnemerspatroon

In het waarnemerspatroon meldt uw primaire object (ook wel waarneembaar genoemd) andere geïnteresseerde objecten (ook wel waarnemers genoemd) met relevante informatie (gebeurtenissen).

Patroon Publiceren/abonneren (Pub/Sub)

Het doel van het patroon Publiceren/Abonneren is hetzelfde als het waarnemerspatroon: u wilt andere services waarschuwen wanneer bepaalde gebeurtenissen plaatsvinden. Maar er is een belangrijk verschil tussen de waarnemer- en pub-/subpatronen. In het waarnemerspatroon wordt de uitzending rechtstreeks van de waarnemers uitgevoerd, zodat ze elkaar "kennen". Maar wanneer u een Pub/Sub-patroon gebruikt, is er een derde onderdeel, broker of berichtenbroker of gebeurtenisbus, dat bekend is door zowel de uitgever als de abonnee. Daarom worden bij het gebruik van het Pub/Sub-patroon de uitgever en de abonnees nauwkeurig ontkoppeld dankzij de vermelde gebeurtenisbus of berichtbroker.

De middleman of event bus

Hoe bereikt u anonimiteit tussen uitgever en abonnee? Een eenvoudige manier is een middleman te laten zorgen voor alle communicatie. Een gebeurtenisbus is zo'n middleman.

Een gebeurtenisbus bestaat doorgaans uit twee delen:

  • De abstractie of interface.

  • Een of meer implementaties.

In afbeelding 6-19 kunt u zien hoe, vanuit een toepassingspunt, de gebeurtenisbus niets meer is dan een Pub/Sub-kanaal. De manier waarop u deze asynchrone communicatie implementeert, kan variëren. Het kan meerdere implementaties hebben, zodat u ertussen kunt wisselen, afhankelijk van de omgevingsvereisten (bijvoorbeeld productie versus ontwikkelomgevingen).

In afbeelding 6-20 ziet u een abstractie van een gebeurtenisbus met meerdere implementaties op basis van infrastructuurberichtentechnologieën zoals RabbitMQ, Azure Service Bus of een andere gebeurtenis-/berichtenbroker.

Diagram showing the addition of an event bus abstraction layer.

Afbeelding 6- 20. Meerdere implementaties van een gebeurtenisbus

Het is handig om de gebeurtenisbus te laten definiëren via een interface, zodat deze kan worden geïmplementeerd met verschillende technologieën, zoals RabbitMQ, Azure Service Bus of anderen. Maar zoals eerder vermeld, is het gebruik van uw eigen abstracties (de event bus-interface) alleen goed als u basisfuncties voor event bus nodig hebt die worden ondersteund door uw abstracties. Als u uitgebreidere servicebusfuncties nodig hebt, moet u waarschijnlijk de API en abstracties gebruiken die worden geleverd door de commerciële servicebus van uw voorkeur in plaats van uw eigen abstracties.

Een Event Bus-interface definiëren

Laten we beginnen met enkele implementatiecode voor de event bus-interface en mogelijke implementaties voor verkenningsdoeleinden. De interface moet algemeen en eenvoudig zijn, zoals in de volgende interface.

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;
}

De Publish methode is eenvoudig. De gebeurtenisbus verzendt de integratiegebeurtenis die eraan is doorgegeven aan elke microservice, of zelfs een externe toepassing, die is geabonneerd op die gebeurtenis. Deze methode wordt gebruikt door de microservice die de gebeurtenis publiceert.

De Subscribe methoden (u kunt verschillende implementaties hebben, afhankelijk van de argumenten) worden gebruikt door de microservices die gebeurtenissen willen ontvangen. Deze methode heeft twee argumenten. De eerste is de integratie-gebeurtenis waarop u zich wilt abonneren (IntegrationEvent). Het tweede argument is de integratiegebeurtenishandler (of callback-methode), genaamd IIntegrationEventHandler<T>, die moet worden uitgevoerd wanneer de ontvanger microservice dat bericht van de integratiegebeurtenis ontvangt.

Aanvullende bronnen

Enkele oplossingen voor berichten die gereed zijn voor productie: