Share via


Communicatie in een microservicearchitectuur

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.

In een monolithische toepassing die op één proces wordt uitgevoerd, roepen onderdelen elkaar aan met behulp van methode of functie-aanroepen op taalniveau. Deze kunnen sterk worden gekoppeld als u objecten met code maakt (bijvoorbeeld new ClassName()), of op een ontkoppelde manier kunt aanroepen als u afhankelijkheidsinjectie gebruikt door te verwijzen naar abstracties in plaats van concrete objectexemplaren. In beide gevallen worden de objecten binnen hetzelfde proces uitgevoerd. De grootste uitdaging bij het wijzigen van een monolithische toepassing in een toepassing op basis van microservices ligt in het wijzigen van het communicatiemechanisme. Een directe conversie van in-process-methode-aanroepen naar RPC-aanroepen naar services leidt tot een snelle en niet efficiënte communicatie die niet goed presteert in gedistribueerde omgevingen. De uitdagingen van het goed ontwerpen van gedistribueerd systeem zijn goed genoeg bekend dat er zelfs een canon is die bekend staat als de Fallacies van gedistribueerde computing die veronderstellingen vermeldt die ontwikkelaars vaak maken wanneer ze overstappen van monolithische naar gedistribueerde ontwerpen.

Er is geen oplossing, maar meerdere. Eén oplossing omvat het zoveel mogelijk isoleren van de zakelijke microservices. Vervolgens gebruikt u asynchrone communicatie tussen de interne microservices en vervangt u verfijnde communicatie die typisch is in intraprocescommunicatie tussen objecten met grofer communicatie. U kunt dit doen door oproepen te groeperen en door gegevens te retourneren die de resultaten van meerdere interne aanroepen aggregeren naar de client.

Een toepassing op basis van microservices is een gedistribueerd systeem dat wordt uitgevoerd op meerdere processen of services, meestal zelfs op meerdere servers of hosts. Elk service-exemplaar is doorgaans een proces. Daarom moeten services communiceren met behulp van een communicatieprotocol tussen processen, zoals HTTP, AMQP of een binair protocol zoals TCP, afhankelijk van de aard van elke service.

De microservicecommunity bevordert de filosofie van 'slimme eindpunten en domme pijpen'. Deze slogan moedigt een ontwerp aan dat zo ontkoppeld mogelijk is tussen microservices en zo samenhangend mogelijk binnen één microservice. Zoals eerder uitgelegd, is elke microservice eigenaar van zijn eigen gegevens en een eigen domeinlogica. Maar de microservices die een end-to-end-toepassing opstellen, worden meestal gewoon gechoreografeerd met behulp van REST-communicatie in plaats van complexe protocollen zoals WS-* en flexibele, gebeurtenisgestuurde communicatie in plaats van gecentraliseerde business-process-orchestrators.

De twee veelgebruikte protocollen zijn HTTP-aanvraag/-respons met resource-API's (bij het uitvoeren van query's op het grootste deel) en lichtgewicht asynchrone berichten bij het communiceren van updates over meerdere microservices. Deze worden in de volgende secties uitgebreid beschreven.

Communicatietypen

Client en services kunnen communiceren via veel verschillende soorten communicatie, die elk gericht zijn op een ander scenario en doelstellingen. In eerste instantie kunnen deze typen communicatie in twee assen worden geclassificeerd.

De eerste as bepaalt of het protocol synchroon of asynchroon is:

  • Synchroon protocol. HTTP is een synchroon protocol. De client verzendt een aanvraag en wacht op een reactie van de service. Dat is onafhankelijk van de uitvoering van clientcode die synchroon kan zijn (thread wordt geblokkeerd) of asynchroon (thread wordt niet geblokkeerd en het antwoord zal uiteindelijk een callback bereiken). Het belangrijkste punt hier is dat het protocol (HTTP/HTTPS) synchroon is en dat de clientcode alleen de taak kan voortzetten wanneer het antwoord van de HTTP-server wordt ontvangen.

  • Asynchroon protocol. Andere protocollen, zoals AMQP (een protocol dat wordt ondersteund door veel besturingssystemen en cloudomgevingen), maken gebruik van asynchrone berichten. De clientcode of de afzender van het bericht wacht meestal niet op een antwoord. Het bericht wordt alleen verzonden als bij het verzenden van een bericht naar een RabbitMQ-wachtrij of een andere berichtenbroker.

De tweede as bepaalt of de communicatie één ontvanger of meerdere ontvangers heeft:

  • Eén ontvanger. Elke aanvraag moet worden verwerkt door precies één ontvanger of service. Een voorbeeld van deze communicatie is het opdrachtpatroon.

  • Meerdere ontvangers. Elke aanvraag kan worden verwerkt door nul tot meerdere ontvangers. Dit type communicatie moet asynchroon zijn. Een voorbeeld is het mechanisme voor publiceren/abonneren dat wordt gebruikt in patronen zoals gebeurtenisgestuurde architectuur. Dit is gebaseerd op een event-bus-interface of berichtbroker bij het doorgeven van gegevensupdates tussen meerdere microservices via gebeurtenissen; het wordt meestal geïmplementeerd via een servicebus of vergelijkbaar artefact zoals Azure Service Bus met behulp van onderwerpen en abonnementen.

Een microservicetoepassing gebruikt vaak een combinatie van deze communicatiestijlen. Het meest voorkomende type is communicatie met één ontvanger met een synchroon protocol zoals HTTP/HTTPS bij het aanroepen van een reguliere Web API HTTP-service. Microservices gebruiken doorgaans ook berichtenprotocollen voor asynchrone communicatie tussen microservices.

Deze assen zijn goed te weten, dus u hebt duidelijkheid over de mogelijke communicatiemechanismen, maar ze zijn niet de belangrijkste zorgen bij het bouwen van microservices. Noch de asynchrone aard van de uitvoering van clientthreads noch de asynchrone aard van het geselecteerde protocol zijn de belangrijke punten bij het integreren van microservices. Wat belangrijk is, is dat u uw microservices asynchroon kunt integreren met behoud van de onafhankelijkheid van microservices, zoals wordt uitgelegd in de volgende sectie.

Asynchrone microserviceintegratie dwingt de autonomie van de microservice af

Zoals vermeld, is het belangrijke punt bij het bouwen van een toepassing op basis van microservices de manier waarop u uw microservices integreert. In het ideale geval moet u proberen de communicatie tussen de interne microservices te minimaliseren. Hoe minder communicatie tussen microservices, hoe beter. Maar in veel gevallen moet u de microservices integreren. Wanneer u dat moet doen, is de kritieke regel hier dat de communicatie tussen de microservices asynchroon moet zijn. Dat betekent niet dat u een specifiek protocol moet gebruiken (bijvoorbeeld asynchrone berichten versus synchrone HTTP). Het betekent alleen dat de communicatie tussen microservices alleen moet worden uitgevoerd door gegevens asynchroon door te geven, maar probeer niet afhankelijk te zijn van andere interne microservices als onderdeel van de HTTP-aanvraag-/antwoordbewerking van de eerste service.

Indien mogelijk, is nooit afhankelijk van synchrone communicatie (aanvraag/antwoord) tussen meerdere microservices, zelfs niet voor query's. Het doel van elke microservice is autonoom en beschikbaar te zijn voor de clientgebruiker, zelfs als de andere services die deel uitmaken van de end-to-end-toepassing, offline of beschadigd zijn. Als u denkt dat u een aanroep van de ene microservice naar andere microservices moet doen (zoals het uitvoeren van een HTTP-aanvraag voor een gegevensquery) om een antwoord te kunnen bieden op een clienttoepassing, hebt u een architectuur die niet tolerant is wanneer sommige microservices mislukken.

Bovendien is het gebruik van HTTP-afhankelijkheden tussen microservices, zoals bij het maken van lange aanvraag-/responscycli met HTTP-aanvraagketens, zoals wordt weergegeven in het eerste deel van afbeelding 4-15, niet alleen uw microservices niet autonoom, maar ook de prestaties ervan worden beïnvloed zodra een van de services in die keten niet goed presteert.

Hoe meer u synchrone afhankelijkheden toevoegt tussen microservices, zoals queryaanvragen, hoe erger de totale reactietijd voor de client-apps wordt.

Diagram showing three types of communications across microservices.

Afbeelding 4-15. Antipatronen en patronen in communicatie tussen microservices

Zoals in het bovenstaande diagram wordt weergegeven, wordt bij synchrone communicatie een 'keten' van aanvragen gemaakt tussen microservices tijdens het leveren van de clientaanvraag. Dit is een antipatroon. In asynchrone communicatie microservices gebruiken asynchrone berichten of http-polling om te communiceren met andere microservices, maar de clientaanvraag wordt direct verwerkt.

Als uw microservice een extra actie moet indienen in een andere microservice, indien mogelijk, voert u deze actie niet synchroon uit en als onderdeel van de oorspronkelijke microserviceaanvraag en antwoordbewerking. Doe dit in plaats daarvan asynchroon (met behulp van asynchrone berichten of integratie-gebeurtenissen, wachtrijen, enzovoort). Maar roep de actie zo veel mogelijk niet synchroon aan als onderdeel van de oorspronkelijke synchrone aanvraag- en antwoordbewerking.

En ten slotte (en dit is waar de meeste problemen zich voordoen bij het bouwen van microservices), als uw eerste microservice gegevens nodig heeft die oorspronkelijk eigendom zijn van andere microservices, vertrouwt u niet op het maken van synchrone aanvragen voor die gegevens. Repliceer of geef die gegevens (alleen de kenmerken die u nodig hebt) in de database van de eerste service door middel van uiteindelijke consistentie (meestal met behulp van integratiegebeurtenissen, zoals wordt uitgelegd in toekomstige secties).

Zoals eerder is aangegeven in de grenzen van het domeinmodel identificeren voor elke microservicesectie , is het dupliceren van sommige gegevens over verschillende microservices geen onjuist ontwerp, integendeel, wanneer u dat doet, kunt u de gegevens vertalen in de specifieke taal of termen van dat extra domein of gebonden context. In de eShopOnContainers-toepassing hebt u bijvoorbeeld een microservice met de naam identity-api die de meeste gegevens van de gebruiker beheert met een entiteit met de naam User. Wanneer u echter gegevens over de gebruiker in de Ordering microservice wilt opslaan, slaat u deze op als een andere entiteit met de naam Buyer. De Buyer entiteit deelt dezelfde identiteit met de oorspronkelijke User entiteit, maar heeft mogelijk slechts de weinige kenmerken die nodig zijn voor het Ordering domein, en niet het hele gebruikersprofiel.

U kunt elk protocol gebruiken om gegevens asynchroon over microservices te communiceren en door te geven om uiteindelijke consistentie te hebben. Zoals vermeld, kunt u integratie-gebeurtenissen gebruiken met behulp van een gebeurtenisbus of berichtenbroker of u kunt zelfs HTTP gebruiken door de andere services te peilen. Het maakt niet uit. De belangrijke regel is het niet maken van synchrone afhankelijkheden tussen uw microservices.

In de volgende secties worden de meerdere communicatiestijlen beschreven die u kunt gebruiken in een toepassing op basis van microservices.

Communicatiestijlen

Er zijn veel protocollen en keuzes die u voor communicatie kunt gebruiken, afhankelijk van het communicatietype dat u wilt gebruiken. Als u een synchroon communicatiemechanisme op basis van aanvragen/antwoorden gebruikt, zijn protocollen zoals HTTP en REST de meest voorkomende, met name als u uw services publiceert buiten de Docker-host of het microservicecluster. Als u intern communiceert tussen services (binnen uw Docker-host of microservicescluster), wilt u mogelijk ook communicatiemechanismen voor binaire indeling gebruiken (zoals WCF met TCP en binaire indeling). U kunt ook asynchrone communicatiemechanismen op basis van berichten gebruiken, zoals AMQP.

Er zijn ook meerdere berichtindelingen, zoals JSON of XML, of zelfs binaire indelingen, die efficiënter kunnen zijn. Als uw gekozen binaire indeling geen standaard is, is het waarschijnlijk geen goed idee om uw services openbaar te publiceren met die indeling. U kunt een niet-standaardindeling gebruiken voor interne communicatie tussen uw microservices. U kunt dit doen wanneer u communiceert tussen microservices binnen uw Docker-host of microservicecluster (bijvoorbeeld Docker-orchestrators) of voor bedrijfseigen clienttoepassingen die met de microservices communiceren.

Communicatie tussen aanvragen en antwoorden met HTTP en REST

Wanneer een client gebruikmaakt van communicatie tussen aanvragen en antwoorden, wordt er een aanvraag naar een service verzonden, waarna de service de aanvraag verwerkt en een antwoord terugstuurt. Communicatie tussen aanvragen en antwoorden is met name geschikt voor het opvragen van gegevens voor een realtime gebruikersinterface (een live gebruikersinterface) van client-apps. Daarom gebruikt u in een microservicearchitectuur waarschijnlijk dit communicatiemechanisme voor de meeste query's, zoals wordt weergegeven in afbeelding 4-16.

Diagram showing request/response comms for live queries and updates.

Afbeelding 4-16. HTTP-aanvraag-/antwoordcommunicatie gebruiken (synchroon of asynchroon)

Wanneer een client gebruikmaakt van communicatie tussen aanvragen en antwoorden, wordt ervan uitgegaan dat het antwoord binnen een korte tijd binnenkomt, meestal minder dan een seconde of een paar seconden. Voor vertraagde reacties moet u asynchrone communicatie implementeren op basis van berichtpatronen en berichttechnologieën. Dit is een andere benadering die in de volgende sectie wordt uitgelegd.

Een populaire architectuurstijl voor communicatie tussen aanvragen en antwoorden is REST. Deze benadering is gebaseerd op en nauw gekoppeld aan het HTTP-protocol, waarbij HTTP-werkwoorden zoals GET, POST en PUT worden gebruikt. REST is de meestgebruikte communicatiebenadering voor architectuur bij het maken van services. U kunt REST-services implementeren wanneer u ASP.NET Core Web API-services ontwikkelt.

Er is extra waarde bij het gebruik van HTTP REST-services als uw interfacedefinitietaal. Als u bijvoorbeeld Swagger-metagegevens gebruikt om uw service-API te beschrijven, kunt u hulpprogramma's gebruiken die client-stubs genereren die uw services rechtstreeks kunnen detecteren en gebruiken.

Aanvullende bronnen

Push en realtime communicatie op basis van HTTP

Een andere mogelijkheid (meestal voor verschillende doeleinden dan REST) is een realtime en een-op-veel-communicatie met frameworks op een hoger niveau, zoals ASP.NET SignalR en protocollen zoals WebSockets.

Zoals in afbeelding 4-17 wordt weergegeven, betekent realtime HTTP-communicatie dat u servercode kunt hebben die inhoud naar verbonden clients pusht wanneer de gegevens beschikbaar komen, in plaats van dat de server wacht tot een client nieuwe gegevens aanvraagt.

Diagram showing push and real-time comms based on SignalR.

Afbeelding 4-17. Een-op-veel realtime asynchrone berichtcommunicatie

SignalR is een goede manier om realtime communicatie te bereiken voor het pushen van inhoud naar de clients vanaf een back-endserver. Omdat communicatie in realtime is, tonen client-apps de wijzigingen vrijwel onmiddellijk. Dit wordt meestal verwerkt door een protocol, zoals WebSockets, met behulp van veel WebSockets-verbindingen (één per client). Een typisch voorbeeld is wanneer een service een wijziging in de score van een sportspel communiceert met veel clientweb-apps tegelijk.