Tjänst-till-tjänst-kommunikation
Dricks
Det här innehållet är ett utdrag från eBook, Architecting Cloud Native .NET Applications for Azure, tillgängligt på .NET Docs eller som en kostnadsfri nedladdningsbar PDF som kan läsas offline.
När vi flyttar från klientdelsklienten tar vi nu itu med serverdelsmikrotjänster som kommunicerar med varandra.
När du skapar ett molnbaserat program vill du vara känslig för hur serverdelstjänster kommunicerar med varandra. Helst, desto mindre kommunikation mellan tjänster, desto bättre. Undvikande är dock inte alltid möjligt eftersom serverdelstjänster ofta förlitar sig på varandra för att slutföra en åtgärd.
Det finns flera allmänt accepterade metoder för att implementera kommunikation mellan tjänster. Typen av kommunikationsinteraktion avgör ofta den bästa metoden.
Överväg följande interaktionstyper:
Fråga – när en anropsmikrotjänst kräver ett svar från en så kallad mikrotjänst, till exempel "Hej, ge mig köparinformationen för ett visst kund-ID".
Kommando – när den anropande mikrotjänsten behöver en annan mikrotjänst för att utföra en åtgärd men inte kräver något svar, till exempel "Hej, skicka bara den här ordern".
Händelse – när en mikrotjänst, som kallas utgivare, genererar en händelse som har ändrats eller en åtgärd har inträffat. Andra mikrotjänster, som kallas prenumeranter, som är intresserade, kan reagera på händelsen på lämpligt sätt. Utgivaren och prenumeranterna känner inte till varandra.
Mikrotjänstsystem använder vanligtvis en kombination av dessa interaktionstyper vid körning av åtgärder som kräver interaktion mellan tjänster. Låt oss ta en närmare titt på var och en och hur du kan implementera dem.
Frågor
Många gånger kan en mikrotjänst behöva fråga en annan, vilket kräver ett omedelbart svar för att slutföra en åtgärd. En varukorgsmikrotjänst kan behöva produktinformation och ett pris för att lägga till en artikel i varukorgen. Det finns många metoder för att implementera frågeåtgärder.
Meddelandehantering för begäran/svar
Ett alternativ för att implementera det här scenariot är att den anropande serverdelsmikrotjänsten gör direkta HTTP-begäranden till de mikrotjänster som den behöver fråga, som visas i bild 4–8.
Bild 4-8. Direkt HTTP-kommunikation
Även om direkta HTTP-anrop mellan mikrotjänster är relativt enkla att implementera, bör du vara noga med att minimera den här metoden. För att starta är dessa anrop alltid synkrona och blockerar åtgärden tills ett resultat returneras eller begäran överskrids. Det som en gång var fristående, oberoende tjänster, som kunde utvecklas självständigt och distribueras ofta, blir nu kopplade till varandra. I takt med att kopplingen mellan mikrotjänster ökar minskar deras arkitektoniska fördelar.
Att köra en sällan förekommande begäran som gör ett enda direkt HTTP-anrop till en annan mikrotjänst kan vara acceptabelt för vissa system. Högvolymanrop som anropar direkta HTTP-anrop till flera mikrotjänster är dock inte tillrådligt. De kan öka svarstiden och påverka systemets prestanda, skalbarhet och tillgänglighet negativt. Ännu värre är att en lång serie direkt HTTP-kommunikation kan leda till djupa och komplexa kedjor av synkrona mikrotjänstanrop, som visas i bild 4–9:
Bild 4-9. Länka HTTP-frågor
Du kan säkert föreställa dig risken i den design som visas i föregående bild. Vad händer om steg 3 misslyckas? Eller misslyckas steg 8? Hur återställer du? Vad händer om steg 6 är långsamt eftersom den underliggande tjänsten är upptagen? Hur fortsätter du? Även om allt fungerar korrekt kan du tänka på svarstiden som det här anropet skulle medföra, vilket är summan av svarstiden för varje steg.
Den stora graden av koppling i föregående bild tyder på att tjänsterna inte var optimalt modellerade. Det skulle vara bra för teamet att gå tillbaka till sin design.
Mönster för materialiserad vy
Ett populärt alternativ för att ta bort mikrotjänstkoppling är mönstret Materialiserad vy. Med det här mönstret lagrar en mikrotjänst en egen lokal, avnormaliserad kopia av data som ägs av andra tjänster. I stället för att shoppingkorgens mikrotjänst kör frågor mot produktkatalogen och prissättningsmikrotjänsterna, behåller den en egen lokal kopia av dessa data. Det här mönstret eliminerar onödig koppling och förbättrar tillförlitligheten och svarstiden. Hela åtgärden körs i en enda process. Vi utforskar det här mönstret och andra dataproblem i kapitel 5.
Service Aggregator-mönster
Ett annat alternativ för att eliminera mikrotjänst-till-mikrotjänstkoppling är en Aggregator-mikrotjänst, som visas i lila i bild 4-10.
Bild 4-10. Aggregator mikrotjänst
Mönstret isolerar en åtgärd som anropar flera serverdelsmikrotjänster och centraliserar logiken till en specialiserad mikrotjänst. Den lila utcheckningsaggregatorns mikrotjänst i föregående bild samordnar arbetsflödet för utcheckningsåtgärden. Den innehåller anrop till flera serverdelsmikrotjänster i sekvenserad ordning. Data från arbetsflödet aggregeras och returneras till anroparen. Även om den fortfarande implementerar direkta HTTP-anrop minskar aggregatorns mikrotjänst direkta beroenden mellan serverdelsmikrotjänster.
Mönster för begäran/svar
En annan metod för att koppla bort synkrona HTTP-meddelanden är ett mönster för begäran-svar, som använder kökommunikation. Kommunikation med hjälp av en kö är alltid en enkelriktad kanal, där en producent skickar meddelandet och en konsument tar emot det. Med det här mönstret implementeras både en begärankö och svarskö, som visas i bild 4–11.
Bild 4-11. Mönster för begärandesvar
Här skapar meddelandeproducenten ett frågebaserat meddelande som innehåller ett unikt korrelations-ID och placerar det i en begärandekö. Den förbrukande tjänsten tar bort meddelandena, bearbetar dem och placerar svaret i svarskö med samma korrelations-ID. Producenttjänsten tar bort meddelandet, matchar det med korrelations-ID:t och fortsätter bearbetningen. Vi tar upp köer i detalj i nästa avsnitt.
Kommandon
En annan typ av kommunikationsinteraktion är ett kommando. En mikrotjänst kan behöva en annan mikrotjänst för att utföra en åtgärd. Mikrotjänsten Ordering kan behöva mikrotjänsten Shipping för att skapa en leverans för en godkänd order. I bild 4–12 skickar en mikrotjänst, kallad Producent, ett meddelande till en annan mikrotjänst, konsumenten, som befaller den att göra något.
Bild 4-12. Kommandointeraktion med en kö
Oftast kräver inte producenten något svar och kan utlösa och glömma meddelandet. Om ett svar behövs skickar konsumenten ett separat meddelande tillbaka till Producent på en annan kanal. Ett kommandomeddelande skickas bäst asynkront med en meddelandekö. stöds av en enkel meddelandekö. I föregående diagram bör du notera hur en kö separerar och frikopplar båda tjänsterna.
Eftersom många meddelandeköer kan skicka samma meddelande mer än en gång, som kallas leverans minst en gång, måste konsumenten kunna identifiera och hantera dessa scenarier korrekt med hjälp av relevanta mönster för idempotent meddelandebearbetning.
En meddelandekö är en mellanhandskonstruktion genom vilken en producent och konsument skickar ett meddelande. Köer implementerar ett asynkront meddelandemönster från punkt till punkt. Producenten vet var ett kommando måste skickas och dirigerar på lämpligt sätt. Kön garanterar att ett meddelande bearbetas av exakt en av de konsumentinstanser som läser från kanalen. I det här scenariot kan antingen producent- eller konsumenttjänsten skala ut utan att påverka den andra. Dessutom kan teknikerna vara olika på varje sida, vilket innebär att vi kan ha en Java-mikrotjänst som anropar en Golang-mikrotjänst .
I kapitel 1 talade vi om att stödja tjänster. Stödtjänster är underordnade resurser som molnbaserade system är beroende av. Meddelandeköer säkerhetskopierar tjänster. Azure-molnet stöder två typer av meddelandeköer som dina molnbaserade system kan använda för att implementera kommandomeddelanden: Azure Storage Queues och Azure Service Bus Queues.
Azure Storage-köer
Azure Storage-köer erbjuder en enkel köinfrastruktur som är snabb, prisvärd och som backas upp av Azure-lagringskonton.
Azure Storage Queues har en REST-baserad kömekanism med tillförlitliga och beständiga meddelanden. De ger en minimal funktionsuppsättning, men är billiga och lagrar miljontals meddelanden. Deras kapacitet är upp till 500 TB. Ett enda meddelande kan vara upp till 64 kB stort.
Du kan komma åt meddelanden var som helst i världen via autentiserade anrop med HTTP eller HTTPS. Lagringsköer kan skalas ut till ett stort antal samtidiga klienter för att hantera trafiktoppar.
Med detta sagt finns det begränsningar med tjänsten:
Meddelandeordningen är inte garanterad.
Ett meddelande kan bara finnas kvar i sju dagar innan det tas bort automatiskt.
Stöd för tillståndshantering, dubblettidentifiering eller transaktioner är inte tillgängligt.
Bild 4–13 visar hierarkin för en Azure Storage-kö.
Bild 4-13. Lagringsköhierarki
I föregående bild bör du notera hur lagringsköer lagrar sina meddelanden i det underliggande Azure Storage-kontot.
För utvecklare tillhandahåller Microsoft flera klient- och serverbibliotek för lagringsköbearbetning. De flesta större plattformar stöds, inklusive .NET, Java, JavaScript, Ruby, Python och Go. Utvecklare bör aldrig kommunicera direkt med dessa bibliotek. Om du gör det kopplas din mikrotjänstkod tätt till Azure Storage Queue-tjänsten. Det är en bättre metod att isolera implementeringsinformationen för API:et. Introducera ett förmedlingslager, eller mellanliggande API, som exponerar generiska åtgärder och kapslar in betongbiblioteket. Med den här lösa kopplingen kan du växla ut en kötjänst mot en annan utan att behöva göra ändringar i huvudlinjetjänstens kod.
Azure Storage-köer är ett ekonomiskt alternativ för att implementera kommandomeddelanden i dina molnbaserade program. Särskilt när en köstorlek överskrider 80 GB, eller om en enkel funktionsuppsättning är acceptabel. Du betalar bara för lagringen av meddelandena. Det finns inga fasta timavgifter.
Azure Service Bus-köer
Mer komplexa meddelandekrav finns i Azure Service Bus-köer.
Azure Service Bus ligger ovanpå en robust meddelandeinfrastruktur och har stöd för en asynkron meddelandemodell. Meddelanden lagras på ett tillförlitligt sätt i en asynkron meddelandekö (kön) tills de tas emot av konsumenten. Kön garanterar FIFO-meddelandeleverans (First-In/First-Out) med respekt för den ordning i vilken meddelanden lades till i kön.
Storleken på ett meddelande kan vara mycket större, upp till 256 KB. Meddelanden sparas i kön under en obegränsad tidsperiod. Service Bus stöder inte bara HTTP-baserade anrop, utan ger även fullständigt stöd för AMQP-protokollet. AMQP är en öppen standard mellan leverantörer som stöder ett binärt protokoll och högre grad av tillförlitlighet.
Service Bus innehåller en omfattande uppsättning funktioner, inklusive transaktionsstöd och en dubblettidentifieringsfunktion. Kön garanterar "högst en gång leverans" per meddelande. Det tar automatiskt bort ett meddelande som redan har skickats. Om en producent är osäker kan den skicka samma meddelande igen och Service Bus garanterar att endast en kopia bearbetas. Dubblettidentifiering gör att du inte behöver skapa ytterligare infrastruktur-VVS.
Ytterligare två företagsfunktioner är partitionering och sessioner. En konventionell Service Bus-kö hanteras av en enda meddelandekö och lagras i ett enda meddelandearkiv. Men Service Bus-partitionering sprider kön över flera meddelandeköer och meddelandelager. Det övergripande dataflödet begränsas inte längre av prestanda för en enda meddelandekö eller ett meddelandearkiv. Ett tillfälligt avbrott i ett meddelandearkiv gör inte en partitionerad kö otillgänglig.
Service Bus-sessioner är ett sätt att gruppera meddelanden. Föreställ dig ett arbetsflödesscenario där meddelanden måste bearbetas tillsammans och åtgärden slutförs i slutet. För att kunna dra nytta av detta måste sessioner uttryckligen aktiveras för kön och varje relaterat meddelande måste innehålla samma sessions-ID.
Det finns dock några viktiga varningar: Service Bus-köernas storlek är begränsad till 80 GB, vilket är mycket mindre än vad som är tillgängligt från butiksköer. Dessutom medför Service Bus-köer en baskostnad och en avgift per åtgärd.
Bild 4–14 beskriver arkitekturen på hög nivå i en Service Bus-kö.
Bild 4-14. Service Bus-kö
Observera punkt-till-punkt-relationen i föregående bild. Två instanser av samma provider placerar meddelanden i en enda Service Bus-kö. Varje meddelande används bara av en av tre konsumentinstanser till höger. Därefter diskuterar vi hur du implementerar meddelanden där olika konsumenter kan vara intresserade av samma meddelande.
Händelser
Meddelandeköer är ett effektivt sätt att implementera kommunikation där en producent asynkront kan skicka ett meddelande till en konsument. Men vad händer när många olika konsumenter är intresserade av samma meddelande? En dedikerad meddelandekö för varje konsument skulle inte skalas bra och skulle bli svår att hantera.
För att åtgärda det här scenariot går vi vidare till den tredje typen av meddelandeinteraktion, händelsen. En mikrotjänst meddelar att en åtgärd har inträffat. Andra mikrotjänster, om de är intresserade, reagerar på åtgärden eller händelsen. Detta kallas även för den händelsedrivna arkitekturstilen.
Händelsehantering är en process i två steg. För en viss tillståndsändring publicerar en mikrotjänst en händelse till en meddelandekö, vilket gör den tillgänglig för alla andra intresserade mikrotjänster. Den intresserade mikrotjänsten meddelas genom att prenumerera på händelsen i meddelandekoordinatorn. Du använder mönstret Publicera/prenumerera för att implementera händelsebaserad kommunikation.
Bild 4–15 visar en mikrotjänst för varukorgen som publicerar ett evenemang med två andra mikrotjänster som prenumererar på den.
Bild 4-15. Händelsedrivna meddelanden
Observera händelsebusskomponenten som finns mitt i kommunikationskanalen. Det är en anpassad klass som kapslar in meddelandekoordinatorn och frikopplar den från det underliggande programmet. Beställnings- och inventeringsmikrotjänsterna driver evenemanget oberoende utan kunskap om varandra eller varukorgens mikrotjänst. När den registrerade händelsen publiceras till händelsebussen agerar de på den.
Med eventing går vi från köteknik till ämnen. Ett ämne liknar en kö, men stöder ett en-till-många-meddelandemönster. En mikrotjänst publicerar ett meddelande. Flera prenumererande mikrotjänster kan välja att ta emot och agera på det meddelandet. Bild 4–16 visar en ämnesarkitektur.
Bild 4-16. Ämnesarkitektur
I föregående bild skickar utgivare meddelanden till ämnet. I slutet får prenumeranter meddelanden från prenumerationer. I mitten vidarebefordrar ämnet meddelanden till prenumerationer baserat på en uppsättning regler som visas i mörkblå rutor. Regler fungerar som ett filter som vidarebefordrar specifika meddelanden till en prenumeration. Här skickas en "GetPrice"-händelse till priset och loggningsprenumerationerna eftersom loggningsprenumerationen har valt att ta emot alla meddelanden. En "GetInformation"-händelse skickas till informations- och loggningsprenumerationerna.
Azure-molnet stöder två olika ämnestjänster: Azure Service Bus Topics och Azure EventGrid.
Azure Service Bus-avsnitt
Azure Service Bus-ämnen finns ovanpå samma robusta asynkrona meddelandemodell för Azure Service Bus-köer. Ett ämne kan ta emot meddelanden från flera oberoende utgivare och skicka meddelanden till upp till 2 000 prenumeranter. Prenumerationer kan läggas till eller tas bort dynamiskt vid körning utan att systemet stoppas eller ämnet återskapas.
Många avancerade funktioner från Azure Service Bus-köer är också tillgängliga för ämnen som duplicerad identifiering och transaktionsstöd. Som standard hanteras Service Bus-ämnen av en enda meddelandekö och lagras i ett enda meddelandearkiv. Men Service Bus-partitionering skalar ett ämne genom att sprida det över många meddelandeköer och meddelandelager.
Schemalagd meddelandeleverans taggar ett meddelande med en viss tid för bearbetning. Meddelandet visas inte i ämnet före den tidpunkten. Med meddelandeuppskjutning kan du skjuta upp en hämtning av ett meddelande till en senare tidpunkt. Båda används ofta i arbetsflödesbearbetningsscenarier där åtgärder bearbetas i en viss ordning. Du kan skjuta upp bearbetningen av mottagna meddelanden tills tidigare arbete har slutförts.
Service Bus-ämnen är en robust och beprövad teknik för att aktivera publicerings-/prenumerationskommunikation i dina molnbaserade system.
Azure Event Grid
Azure Service Bus är en stridstestad meddelandekö med en fullständig uppsättning företagsfunktioner, men Azure Event Grid är den nya killen i kvarteret.
Vid första anblicken kan Event Grid se ut som ett annat ämnesbaserat meddelandesystem. Men det är annorlunda på många sätt. Med fokus på händelsedrivna arbetsbelastningar möjliggör det händelsebearbetning i realtid, djup Azure-integrering och en öppen plattform – allt på serverlös infrastruktur. Den är utformad för moderna molnbaserade och serverlösa program
Som ett centraliserat backplan för händelser, eller pipe, reagerar Event Grid på händelser i Azure-resurser och från dina egna tjänster.
Händelsemeddelanden publiceras till ett Event Grid-ämne som i sin tur dirigerar varje händelse till en prenumeration. Prenumeranter mappar till prenumerationer och använder händelserna. Precis som Service Bus stöder Event Grid en filtrerad prenumerantmodell där en prenumeration anger en regel för de händelser som den vill ta emot. Event Grid ger snabbt dataflöde med en garanti på 10 miljoner händelser per sekund som möjliggör leverans i nära realtid – mycket mer än vad Azure Service Bus kan generera.
En bra punkt för Event Grid är dess djupa integrering i infrastrukturresurserna i Azure. En Azure-resurs, till exempel Cosmos DB, kan publicera inbyggda händelser direkt till andra intresserade Azure-resurser – utan att behöva anpassad kod. Event Grid kan publicera händelser från en Azure-prenumeration, resursgrupp eller tjänst, vilket ger utvecklare detaljerad kontroll över livscykeln för molnresurser. Event Grid är dock inte begränsat till Azure. Det är en öppen plattform som kan använda anpassade HTTP-händelser som publicerats från program eller tjänster från tredje part och dirigera händelser till externa prenumeranter.
När du publicerar och prenumererar på interna händelser från Azure-resurser krävs ingen kodning. Med enkel konfiguration kan du integrera händelser från en Azure-resurs till en annan med inbyggd VVS för ämnen och prenumerationer. Bild 4–17 visar händelserutnätets anatomi.
Bild 4-17. Event Grid-anatomi
En stor skillnad mellan EventGrid och Service Bus är det underliggande mönstret för meddelandeutbyte.
Service Bus implementerar en äldre typ av pull-modell där den underordnade prenumeranten aktivt avsöker ämnesprenumerationen efter nya meddelanden. På uppsidan ger den här metoden prenumeranten full kontroll över den takt i vilken den bearbetar meddelanden. Den styr när och hur många meddelanden som ska bearbetas vid en viss tidpunkt. Olästa meddelanden finns kvar i prenumerationen tills de bearbetas. En betydande brist är svarstiden mellan den tidpunkt då händelsen genereras och avsökningsåtgärden som hämtar meddelandet till prenumeranten för bearbetning. Dessutom förbrukar omkostnaderna för konstant avsökning för nästa händelse resurser och pengar.
EventGrid är dock annorlunda. Den implementerar en push-modell där händelser skickas till EventHandlers som mottagna, vilket ger händelseleverans i nära realtid. Det minskar också kostnaderna eftersom tjänsten endast utlöses när den behövs för att använda en händelse – inte kontinuerligt som med avsökning. Med detta sagt måste en händelsehanterare hantera den inkommande belastningen och tillhandahålla begränsningsmekanismer för att skydda sig mot att bli överbelastad. Många Azure-tjänster som använder dessa händelser, till exempel Azure Functions och Logic Apps, tillhandahåller automatiska autoskalningsfunktioner för att hantera ökad belastning.
Event Grid är en fullständigt hanterad serverlös molntjänst. Den skalas dynamiskt baserat på din trafik och debiterar dig endast för din faktiska användning, inte förinköpt kapacitet. De första 100 000 åtgärderna per månad är kostnadsfria – åtgärder som definieras som händelse-ingress (inkommande händelsemeddelanden), försök till prenumerationsleverans, hanteringsanrop och filtrering efter ämne. Med 99,99 % tillgänglighet garanterar EventGrid leverans av en händelse inom en 24-timmarsperiod, med inbyggda återförsöksfunktioner för misslyckad leverans. Meddelanden som inte har levererats kan flyttas till en kö med "obeställbara meddelanden" för lösning. Till skillnad från Azure Service Bus är Event Grid justerat för snabba prestanda och stöder inte funktioner som ordnade meddelanden, transaktioner och sessioner.
Strömmande meddelanden i Azure-molnet
Azure Service Bus och Event Grid ger bra stöd för program som exponerar enskilda, diskreta händelser som om ett nytt dokument har infogats i en Cosmos DB. Men vad händer om ditt molnbaserade system behöver bearbeta en ström av relaterade händelser? Händelseströmmar är mer komplexa. De är vanligtvis tidsbeställda, sammankopplade och måste bearbetas som en grupp.
Azure Event Hub är en dataströmningsplattform och händelseinmatningstjänst som samlar in, transformerar och lagrar händelser. Den är finjusterad för att samla in strömmande data, till exempel kontinuerliga händelsemeddelanden som genereras från en telemetrikontext. Tjänsten är mycket skalbar och kan lagra och bearbeta miljontals händelser per sekund. Det visas i bild 4–18 och är ofta en ytterdörr för en händelsepipeline som tar bort inmatningsströmmen från händelseförbrukningen.
Bild 4-18. Azure Event Hub
Event Hub stöder låg svarstid och konfigurerbar tidsbevarande. Till skillnad från köer och ämnen behåller Event Hubs händelsedata när de har lästs av en konsument. Med den här funktionen kan andra dataanalystjänster, både interna och externa, spela upp data för vidare analys. Händelser som lagras i händelsehubben tas bara bort när kvarhållningsperioden har löpt ut, vilket är en dag som standard, men kan konfigureras.
Event Hub stöder vanliga protokoll för händelsepublicering, inklusive HTTPS och AMQP. Den stöder även Kafka 1.0. Befintliga Kafka-program kan kommunicera med Event Hub med hjälp av Kafka-protokollet som ett alternativ till att hantera stora Kafka-kluster. Många molnbaserade system med öppen källkod omfattar Kafka.
Event Hubs implementerar meddelandeströmning via en partitionerad konsumentmodell där varje konsument endast läser en specifik delmängd, eller partition, av meddelandeströmmen. Det här mönstret möjliggör enorm horisontell skalning för händelsebearbetning och ger andra strömfokuserade funktioner som inte är tillgängliga i köer och ämnen. En partition är en ordnad sekvens av händelser som hålls kvar i en händelsehubb. När nyare händelser kommer läggs de till i slutet av den här sekvensen. Bild 4–19 visar partitionering i en händelsehubb.
Bild 4-19. Partitionering av händelsehubb
I stället för att läsa från samma resurs läser varje konsumentgrupp över en delmängd, eller partition, av meddelandeströmmen.
För molnbaserade program som måste strömma ett stort antal händelser kan Azure Event Hub vara en robust och prisvärd lösning.