Asynchrone communicatie op basis van berichten

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.

Asynchrone berichten en gebeurtenisgestuurde communicatie zijn essentieel bij het doorgeven van wijzigingen in meerdere microservices en hun gerelateerde domeinmodellen. Zoals eerder vermeld in de discussie microservices en gebonden contexten (PC's), kunnen modellen (Gebruiker, Klant, Product, Account, enzovoort) verschillende dingen betekenen voor verschillende microservices of PC's. Dat betekent dat wanneer er wijzigingen optreden, u een manier nodig hebt om wijzigingen in de verschillende modellen af te stemmen. Een oplossing is uiteindelijke consistentie en gebeurtenisgestuurde communicatie op basis van asynchrone berichten.

Wanneer u berichten gebruikt, communiceren processen door berichten asynchroon uit te wisselen. Een client doet een opdracht of een aanvraag naar een service door het een bericht te verzenden. Als de service moet reageren, wordt er een ander bericht naar de client verzonden. Omdat het een op berichten gebaseerde communicatie is, gaat de client ervan uit dat het antwoord niet onmiddellijk wordt ontvangen en dat er helemaal geen antwoord is.

Een bericht wordt samengesteld door een koptekst (metagegevens zoals identificatie- of beveiligingsgegevens) en een hoofdtekst. Berichten worden meestal verzonden via asynchrone protocollen zoals AMQP.

De voorkeursinfrastructuur voor dit type communicatie in de microservicescommunity is een lichtgewicht berichtenbroker, die verschilt van de grote brokers en orchestrators die worden gebruikt in SOA. In een lichtgewicht berichtbroker is de infrastructuur doorgaans 'dom', die alleen fungeert als een berichtenbroker, met eenvoudige implementaties zoals RabbitMQ of een schaalbare servicebus in de cloud, zoals Azure Service Bus. In dit scenario bevindt de meeste 'slimme' denkwijze zich nog steeds in de eindpunten die berichten produceren en verbruiken, namelijk in de microservices.

Een andere regel die u zoveel mogelijk moet volgen, is om alleen asynchrone berichten tussen de interne services te gebruiken en om alleen synchrone communicatie (zoals HTTP) van de client-apps naar de front-endservices (API Gateways plus het eerste niveau van microservices) te gebruiken.

Er zijn twee soorten asynchrone berichtencommunicatie: communicatie op basis van één ontvangerbericht en communicatie op basis van berichten van meerdere ontvangers. In de volgende secties vindt u meer informatie over deze secties.

Communicatie op basis van berichten met één ontvanger

Op berichten gebaseerde asynchrone communicatie met één ontvanger betekent dat er punt-naar-punt-communicatie is die een bericht levert aan precies een van de consumenten die vanuit het kanaal lezen en dat het bericht slechts één keer wordt verwerkt. Er zijn echter speciale situaties. In een cloudsysteem dat probeert automatisch te herstellen van fouten, kan hetzelfde bericht meerdere keren opnieuw worden verzonden. Vanwege netwerkfouten moet de client het verzenden van berichten opnieuw kunnen proberen en moet de server een bewerking implementeren om idempotent te zijn om een bepaald bericht slechts één keer te kunnen verwerken.

Communicatie op basis van berichten met één ontvanger is met name geschikt voor het verzenden van asynchrone opdrachten van de ene microservice naar de andere, zoals wordt weergegeven in afbeelding 4-18 die deze benadering illustreert.

Zodra u op berichten gebaseerde communicatie (met opdrachten of gebeurtenissen) begint te verzenden, moet u voorkomen dat communicatie op basis van berichten wordt gemengd met synchrone HTTP-communicatie.

A single microservice receiving an asynchronous message

Afbeelding 4-18. Eén microservice ontvangt een asynchroon bericht

Wanneer de opdrachten afkomstig zijn van clienttoepassingen, kunnen ze worden geïmplementeerd als synchrone HTTP-opdrachten. Gebruik opdrachten op basis van berichten wanneer u een hogere schaalbaarheid nodig hebt of wanneer u zich al in een bedrijfsproces op basis van berichten bevindt.

Communicatie op basis van berichten met meerdere ontvangers

Als flexibelere benadering wilt u mogelijk ook een mechanisme voor publiceren/abonneren gebruiken, zodat uw communicatie van de afzender beschikbaar is voor aanvullende microservices voor abonnees of externe toepassingen. Het helpt u dus om het open/gesloten principe in de verzendende service te volgen. Op die manier kunnen er in de toekomst extra abonnees worden toegevoegd zonder de afzenderservice te hoeven wijzigen.

Wanneer u een communicatie over publiceren/abonneren gebruikt, gebruikt u mogelijk een event bus-interface om gebeurtenissen naar elke abonnee te publiceren.

Asynchrone gebeurtenisgestuurde communicatie

Wanneer u asynchrone gebeurtenisgestuurde communicatie gebruikt, publiceert een microservice een integratiegebeurtenis wanneer er iets binnen het domein gebeurt en moet een andere microservice hiervan op de hoogte zijn, zoals een prijswijziging in een microservice in een productcatalogus. Extra microservices abonneren zich op de gebeurtenissen, zodat ze ze asynchroon kunnen ontvangen. Als dat gebeurt, kunnen de ontvangers hun eigen domeinentiteiten bijwerken, waardoor meer integratie-gebeurtenissen kunnen worden gepubliceerd. Dit systeem voor publiceren/abonneren wordt uitgevoerd met behulp van een implementatie van een gebeurtenisbus. De gebeurtenisbus kan worden ontworpen als een abstractie of interface, met de API die nodig is om zich te abonneren op of af te melden voor gebeurtenissen en om gebeurtenissen te publiceren. De gebeurtenisbus kan ook een of meer implementaties hebben op basis van een interproces- en berichtenbroker, zoals een berichtenwachtrij of servicebus die ondersteuning biedt voor asynchrone communicatie en een model voor publiceren/abonneren.

Als een systeem gebruikmaakt van uiteindelijke consistentie op basis van integratie-gebeurtenissen, wordt aanbevolen dat deze aanpak duidelijk wordt gemaakt voor de eindgebruiker. Het systeem mag geen benadering gebruiken die integratiegebeurtenissen nabootst, zoals SignalR of pollingsystemen van de client. De eindgebruiker en de eigenaar van het bedrijf moeten expliciet consistentie in het systeem omarmen en beseffen dat het bedrijf in veel gevallen geen probleem heeft met deze aanpak, zolang dit expliciet is. Deze benadering is belangrijk omdat gebruikers verwachten dat bepaalde resultaten onmiddellijk worden weergegeven en dit aspect zich mogelijk niet voordoet met uiteindelijke consistentie.

Zoals eerder vermeld in de sectie Uitdagingen en oplossingen voor gedistribueerd gegevensbeheer , kunt u integratiegebeurtenissen gebruiken om zakelijke taken te implementeren die meerdere microservices omvatten. U hebt dus uiteindelijke consistentie tussen deze services. Een uiteindelijk consistente transactie bestaat uit een verzameling gedistribueerde acties. Bij elke actie werkt de gerelateerde microservice een domeinentiteit bij en publiceert een andere integratiegebeurtenis die de volgende actie binnen dezelfde end-to-end zakelijke taak genereert.

Een belangrijk punt is dat u mogelijk wilt communiceren met meerdere microservices die zijn geabonneerd op dezelfde gebeurtenis. Hiervoor kunt u berichten publiceren/abonneren op basis van gebeurtenisgestuurde communicatie, zoals weergegeven in afbeelding 4-19. Dit mechanisme voor publiceren/abonneren is niet exclusief voor de microservicearchitectuur. Het is vergelijkbaar met de manier waarop gebonden contexten in DDD moeten communiceren, of naar de manier waarop u updates van de schrijfdatabase doorgeeft aan de leesdatabase in het architectuurpatroon Command and Query Responsibility Segregation (CQRS). Het doel is om uiteindelijke consistentie te hebben tussen meerdere gegevensbronnen in uw gedistribueerde systeem.

Diagram showing asynchronous event-driven communications.

Afbeelding 4-19. Asynchrone gebeurtenisgestuurde berichtcommunicatie

In asynchrone gebeurtenisgestuurde communicatie publiceert één microservice gebeurtenissen naar een gebeurtenisbus en veel microservices kunnen zich hierop abonneren, om hiervan op de hoogte te worden gesteld en erop te reageren. Uw implementatie bepaalt welk protocol moet worden gebruikt voor op gebeurtenissen gebaseerde, berichtengebaseerde communicatie. AMQP kan helpen bij het bereiken van betrouwbare communicatie in de wachtrij.

Wanneer u een gebeurtenisbus gebruikt, kunt u een abstractieniveau (zoals een event bus-interface) gebruiken op basis van een gerelateerde implementatie in klassen met code met behulp van de API van een berichtenbroker zoals RabbitMQ of een servicebus zoals Azure Service Bus met onderwerpen. U kunt ook een servicebus op een hoger niveau gebruiken, zoals NServiceBus, MassTransit of Brighter om uw gebeurtenisbus te formuleren en het systeem te publiceren/abonneren.

Een opmerking over berichtentechnologieën voor productiesystemen

De berichtentechnologieën die beschikbaar zijn voor het implementeren van uw abstracte gebeurtenisbus, bevinden zich op verschillende niveaus. Producten zoals RabbitMQ (een berichtenbrokertransport) en Azure Service Bus bevinden zich bijvoorbeeld op een lager niveau dan andere producten, zoals NServiceBus, MassTransit of Brighter, die op RabbitMQ en Azure Service Bus kunnen werken. Uw keuze is afhankelijk van het aantal uitgebreide functies op toepassingsniveau en kant-en-klare schaalbaarheid die u nodig hebt voor uw toepassing. Voor het implementeren van alleen een proof-of-concept event bus voor uw ontwikkelomgeving, zoals deze is gedaan in het voorbeeld eShopOnContainers, is een eenvoudige implementatie bovenop RabbitMQ die wordt uitgevoerd op een Docker-container mogelijk voldoende.

Voor bedrijfskritieke en productiesystemen die hyperschaalbaarheid nodig hebben, kunt u Azure Service Bus echter evalueren. Voor abstracties en functies op hoog niveau die de ontwikkeling van gedistribueerde toepassingen eenvoudiger maken, raden we u aan om andere commerciële en opensource-servicebussen, zoals NServiceBus, MassTransit en Brighter, te evalueren. Natuurlijk kunt u uw eigen servicebusfuncties bouwen op basis van technologieën op lager niveau, zoals RabbitMQ en Docker. Maar dat loodgieterswerk kan te veel kosten voor een aangepaste bedrijfstoepassing.

Flexibel publiceren naar de gebeurtenisbus

Een uitdaging bij het implementeren van een gebeurtenisgestuurde architectuur in meerdere microservices is het atomisch bijwerken van de status in de oorspronkelijke microservice terwijl de gerelateerde integratiegebeurtenis flexibel wordt gepubliceerd in de gebeurtenisbus, op een of andere manier op basis van transacties. Hier volgen enkele manieren om deze functionaliteit te bereiken, hoewel er ook aanvullende benaderingen kunnen zijn.

  • Een transactionele wachtrij (op basis van DTC) gebruiken, zoals MSMQ. (Dit is echter een verouderde benadering.)

  • Transactielogboekanalyse gebruiken.

  • Het volledige patroon Gebeurtenisbronnen gebruiken.

  • Met behulp van het postvak UIT-patroon: een transactionele databasetabel als een berichtenwachtrij die de basis vormt voor een gebeurtenismakeronderdeel waarmee de gebeurtenis wordt gemaakt en gepubliceerd.

Zie Het Data-platform voor bedrijfskritieke workloads in Azure voor een volledige beschrijving van de uitdagingen in deze ruimte, waaronder hoe berichten met mogelijk onjuiste gegevens kunnen worden gepubliceerd.

Aanvullende onderwerpen die u moet overwegen bij het gebruik van asynchrone communicatie zijn bericht-idempotentie en berichtontdubbeling. Deze onderwerpen worden besproken in de sectie Op gebeurtenissen gebaseerde communicatie tussen microservices (integratiegebeurtenissen) verderop in deze handleiding implementeren.

Aanvullende bronnen