Den här artikeln innehåller vägledning för att optimera skalbarhet och prestanda när du använder Azure Event Hubs och Azure Functions tillsammans i dina program.
Funktionsgruppering
Vanligtvis kapslar en funktion in en arbetsenhet i en händelsebearbetningsström. En funktion kan till exempel omvandla en händelse till en ny datastruktur eller berika data för underordnade program.
I Functions tillhandahåller en funktionsapp körningskontexten för funktioner. Funktionsappbeteenden gäller för alla funktioner som funktionsappen är värd för. Funktioner i en funktionsapp distribueras tillsammans och skalas tillsammans. Alla funktioner i en funktionsapp måste ha samma språk.
Hur du grupperar funktioner i funktionsappar kan påverka prestanda- och skalningsfunktionerna i dina funktionsappar. Du kan gruppera efter åtkomsträttigheter, distribution och användningsmönster som anropar koden.
Vägledning om metodtips för funktioner för gruppering och andra aspekter finns i Metodtips för tillförlitliga Azure Functions och Förbättra prestanda och tillförlitlighet för Azure Functions.
Följande lista är vägledning för grupperingsfunktioner. Vägledningen tar hänsyn till lagrings- och konsumentgruppsaspekter:
Värd för en enskild funktion i en funktionsapp: Om Event Hubs utlöser en funktion kan du isolera funktionen i sin egen funktionsapp för att minska konkurrensen mellan den funktionen och andra funktioner. Isolering är särskilt viktigt om de andra funktionerna är processor- eller minnesintensiva. Den här tekniken hjälper eftersom varje funktion har sitt eget minnesfotavtryck och användningsmönster som direkt kan påverka skalningen av funktionsappen som är värd för den.
Ge varje funktionsapp ett eget lagringskonto: Undvik att dela lagringskonton mellan funktionsappar. Om en funktionsapp använder ett lagringskonto ska du inte heller använda det kontot för andra lagringsåtgärder eller behov. Det kan vara särskilt viktigt att undvika att dela lagringskonton för funktioner som Event Hubs utlöser, eftersom sådana funktioner kan ha stora mängder lagringstransaktioner på grund av kontrollpunkter.
Skapa en dedikerad konsumentgrupp för varje funktionsapp: En konsumentgrupp är en vy över en händelsehubb. Olika konsumentgrupper har olika vyer, vilket innebär att tillstånd, positioner och förskjutningar kan skilja sig åt. Konsumentgrupper gör det möjligt för flera förbrukande program att ha egna vyer över händelseströmmen och att läsa strömmen oberoende av varandra i sin egen takt och med sina egna förskjutningar. Mer information om konsumentgrupper finns i Funktioner och terminologi i Azure Event Hubs.
En konsumentgrupp har ett eller flera konsumentprogram som är associerade med den, och ett konsumentprogram kan använda en eller flera konsumentgrupper. I en dataströmbearbetningslösning motsvarar varje konsumentprogram en konsumentgrupp. En funktionsapp är ett utmärkt exempel på ett konsumentprogram. Följande diagram innehåller ett exempel på två funktionsappar som läser från en händelsehubb, där varje app har en egen dedikerad konsumentgrupp:
Dela inte konsumentgrupper mellan funktionsappar och andra konsumentprogram. Varje funktionsapp bör vara ett distinkt program med en egen tilldelad konsumentgrupp för att säkerställa förskjutningsintegritet för varje konsument och för att förenkla beroenden i en arkitektur för händelseströmning. En sådan konfiguration, tillsammans med att tillhandahålla varje händelsehubbutlöst funktion sin egen funktionsapp och lagringskonto, hjälper till att lägga grunden för optimal prestanda och skalning.
Funktionsvärdplaner
Det finns flera värdalternativ för funktionsappar och det är viktigt att granska deras funktioner. Information om dessa värdalternativ finns i Värdalternativ för Azure Functions. Notera hur alternativen skalas.
Förbrukningsplanen är standard. Funktionsappar i förbrukningsplanen skalas separat och är mest effektiva när de undviker långvariga uppgifter.
Premium- och Dedikerade planer används ofta för att vara värd för flera funktionsappar och funktioner som är mer processor- och minnesintensiva. Med den dedikerade planen kör du dina funktioner i en Azure App Service-plan till vanliga Priser för App Service-abonnemang. Det är viktigt att observera att alla funktionsappar i dessa planer delar de resurser som allokeras till planen. Om funktioner har olika belastningsprofiler eller unika krav är det bäst att vara värd för dem i olika planer, särskilt i dataströmbearbetningsprogram.
Azure Container Apps ger integrerat stöd för att utveckla, distribuera och hantera containerbaserade funktionsappar i Azure Functions. På så sätt kan du köra dina händelsedrivna funktioner i en helt hanterad Kubernetes-baserad miljö med inbyggt stöd för övervakning med öppen källkod, mTLS, Dapr och KEDA.
Event Hubs-skalning
När du distribuerar ett Event Hubs-namnområde finns det flera viktiga inställningar som du måste ange korrekt för att säkerställa högsta prestanda och skalning. Det här avsnittet fokuserar på standardnivån för Event Hubs och de unika funktioner på den nivån som påverkar skalning när du även använder Functions. Mer information om Event Hubs-nivåer finns i Basic jämfört med Standard jämfört med Premium jämfört med dedikerade nivåer.
Ett Event Hubs-namnområde motsvarar ett Kafka-kluster. Information om hur Event Hubs och Kafka relaterar till varandra finns i Vad är Azure Event Hubs för Apache Kafka.
Förstå dataflödesenheter (TUS)
På nivån Event Hubs Standard klassificeras dataflödet som mängden data som anges och läss från namnområdet per tidsenhet. Ru:er är förinköpta enheter med dataflödeskapacitet.
Ru:er debiteras per timme.
Alla händelsehubbar i ett namnområde delar ru:erna. För att kunna beräkna kapacitetsbehoven korrekt måste du överväga alla program och tjänster, både utgivare och konsumenter. Funktioner påverkar antalet byte och händelser som publiceras till och läss från en händelsehubb.
Betoningen för att fastställa antalet TU:er ligger på ingångspunkten. Aggregerade konsumentprogram, inklusive den hastighet med vilken dessa händelser bearbetas, måste dock också inkluderas i beräkningen.
Mer information om Event Hubs-dataflödesenheter finns i Dataflödesenheter.
Skala upp med Auto-inflate
Automatisk inflate kan aktiveras på ett Event Hubs-namnområde för att hantera situationer där belastningen överskrider det konfigurerade antalet TU:er. Automatisk inflate förhindrar begränsning av ditt program och hjälper till att säkerställa att bearbetningen, inklusive inmatning av händelser, fortsätter utan avbrott. Eftersom TU-inställningen påverkar kostnaderna kan du hantera problem med överetablering genom att använda Automatisk uppslag.
Auto-inflate är en funktion i Event Hubs som ofta förväxlas med autoskalning, särskilt i samband med serverlösa lösningar. Men autouppfyllnad, till skillnad från autoskalning, skalas inte ned när den tillagda kapaciteten inte längre behövs.
Om programmet behöver kapacitet som överskrider det maximala tillåtna antalet TU:er kan du överväga att använda Event Hubs Premium-nivån eller den dedikerade nivån.
Partitioner och samtidiga funktioner
När en händelsehubb skapas måste antalet partitioner anges. Partitionsantalet är fortfarande fast och kan inte ändras förutom från premium- och dedikerade nivåer. När Event Hubs utlöser funktionsappar är det möjligt att antalet samtidiga instanser kan vara lika med antalet partitioner.
I Förbruknings- och Premium-värdplaner skalar funktionsappinstanserna ut dynamiskt för att uppfylla antalet partitioner om det behövs. Den dedikerade värdplanen kör funktioner i en App Service-plan och kräver att du konfigurerar dina instanser manuellt eller konfigurerar ett autoskalningsschema. Mer information finns i Dedikerade värdplaner för Azure Functions.
I slutändan är en en-till-en-relation mellan antalet partitioner och funktionsinstanser, eller konsumenter, det idealiska målet för maximalt dataflöde i en dataströmbearbetningslösning. För att uppnå optimal parallellitet, ha flera konsumenter i en konsumentgrupp. För Functions översätts det här målet till många instanser av en funktion i planen. Resultatet kallas parallellitet på partitionsnivå eller den maximala grad av parallellitet som visas i följande diagram:
Det kan verka vettigt att konfigurera så många partitioner som möjligt för att uppnå maximalt dataflöde och ta hänsyn till möjligheten till en högre volym händelser. Det finns dock flera viktiga faktorer att tänka på när du konfigurerar många partitioner:
- Fler partitioner kan leda till mer dataflöde: Eftersom graden av parallellitet är antalet konsumenter (funktionsinstanser), desto fler partitioner finns det, desto högre kan det samtidiga dataflödet vara. Det här är viktigt när du delar ett angivet antal TU:er för en händelsehubb med andra konsumentprogram.
- Fler funktioner kan kräva mer minne: I takt med att antalet funktionsinstanser ökar, ökar även minnesfotavtrycket för resurser i planen. Vid något tillfälle kan för många partitioner försämra prestanda för konsumenter.
- Det finns en risk för mottryck från underordnade tjänster: När mer dataflöde genereras riskerar du att överbelasta nedströmstjänster eller få tillbaka tryck från dem. Uttjäntas för konsumenter måste beaktas när man överväger konsekvenserna för omgivande resurser. Möjliga konsekvenser inkluderade begränsning från andra tjänster, nätverksmättnad och andra former av resurskonkurrering.
- Partitioner kan fyllas i glest: Kombinationen av många partitioner och en låg mängd händelser kan leda till data som är gles fördelade över partitioner. I stället kan ett mindre antal partitioner ge bättre prestanda och resursanvändning.
Tillgänglighet och konsekvens
När en partitionsnyckel eller ett ID inte har angetts dirigerar Event Hubs en inkommande händelse till nästa tillgängliga partition. Den här metoden ger hög tillgänglighet och hjälper till att öka dataflödet för konsumenter.
När beställning av en uppsättning händelser krävs kan händelseproducenten ange att en viss partition ska användas för alla händelser i uppsättningen. Konsumentprogrammet som läser från partitionen tar emot händelserna i rätt ordning. Den här kompromissen ger konsekvens men äventyrar tillgängligheten. Använd inte den här metoden om inte händelseordningen måste bevaras.
För Functions uppnås ordning när händelser publiceras till en viss partition och en händelsehubbutlöst funktion hämtar ett lån till samma partition. För närvarande stöds inte möjligheten att konfigurera en partition med Event Hubs-utdatabindningen. I stället är den bästa metoden att använda en av Event Hubs SDK:erna för att publicera till en specifik partition.
Mer information om hur Event Hubs stöder tillgänglighet och konsekvens finns i Tillgänglighet och konsekvens i Event Hubs.
Event Hubs-utlösare
Det här avsnittet fokuserar på inställningar och överväganden för att optimera funktioner som Event Hubs utlöser. Faktorerna är batchbearbetning, sampling och relaterade funktioner som påverkar beteendet för en utlösarbindning för händelsehubben.
Batchbearbetning för utlösta funktioner
Du kan konfigurera funktioner som en händelsehubb utlöser för att bearbeta en batch händelser eller en händelse i taget. Bearbetning av en batch med händelser kan vara effektivare när det minskar en del av omkostnaderna för funktionsanrop. Om du inte bara behöver bearbeta en enskild händelse bör funktionen konfigureras för att bearbeta flera händelser när den anropas.
Aktivering av batchbearbetning för Event Hubs-utlösarbindningen varierar mellan olika språk:
- JavaScript, Python och andra språk aktiverar batchbearbetning när kardinalitetsegenskapen är inställd på många i function.json-filen för funktionen.
- I C# konfigureras kardinaliteten automatiskt när en matris har angetts för typen i attributet EventHubTrigger .
Mer information om hur batchbearbetning är aktiverat finns i Azure Event Hubs-utlösaren för Azure Functions.
Inställningar för utlösare
Flera konfigurationsinställningar i host.json-filen spelar en viktig roll i prestandaegenskaperna för Event Hubs-utlösarbindningen för Functions:
- maxEventBatchSize: Den här inställningen representerar det maximala antalet händelser som funktionen kan ta emot när den anropas. Om antalet mottagna händelser är mindre än det här beloppet anropas funktionen fortfarande med så många händelser som är tillgängliga. Du kan inte ange en minsta batchstorlek.
- prefetchCount: Antalet prefetch är en av de viktigaste inställningarna när du optimerar för prestanda. Den underliggande AMQP-kanalen refererar till det här värdet för att avgöra hur många meddelanden som ska hämtas och cachelagras för klienten. Prefetch-antalet ska vara större än eller lika med maxEventBatchSize-värdet och är ofta inställt på en multipel av det beloppet. Om du ställer in det här värdet på ett tal som är mindre än maxEventBatchSize-inställningen kan prestandan försämras.
- batchCheckpointFrequency: När funktionen bearbetar batchar avgör det här värdet hur snabbt kontrollpunkter skapas. Standardvärdet är 1, vilket innebär att det finns en kontrollpunkt när en funktion bearbetar en enda batch. En kontrollpunkt skapas på partitionsnivå för varje läsare i konsumentgruppen. Information om hur den här inställningen påverkar repriser och återförsök av händelser finns i Händelsehubb utlöst Azure-funktion: Repriser och återförsök (blogginlägg).
Utför flera prestandatester för att fastställa de värden som ska anges för utlösarbindningen. Vi rekommenderar att du ändrar inställningarna stegvis och mäter konsekvent för att finjustera dessa alternativ. Standardvärdena är en rimlig utgångspunkt för de flesta lösningar för händelsebearbetning.
Kontrollpunkter
Kontrollpunkter markerar eller checkar in läsarpositioner i en partitionshändelsesekvens. Det är functions-värdens ansvar att kontrollera när händelser bearbetas och inställningen för batchkontrollpunktsfrekvensen uppfylls. Mer information om kontrollpunkter finns i Funktioner och terminologi i Azure Event Hubs.
Följande begrepp kan hjälpa dig att förstå relationen mellan kontrollpunkter och hur funktionen bearbetar händelser:
- Undantag räknas fortfarande mot framgång: Om funktionsprocessen inte kraschar under bearbetningen av händelser anses slutförandet av funktionen vara lyckad, även om undantag inträffar. När funktionen är klar utvärderar Functions-värden batchCheckpointFrequency. Om det är dags för en kontrollpunkt skapar den en, oavsett om det fanns undantag. Det faktum att undantag inte påverkar kontrollpunkter bör inte påverka din korrekta användning av undantagskontroll och hantering.
- Batchfrekvens spelar roll: I lösningar för händelseströmning med stora volymer kan det vara fördelaktigt att ändra inställningen batchCheckpointFrequency till ett värde som är större än 1. Om du ökar det här värdet kan du minska antalet skapande av kontrollpunkter och därmed antalet I/O-åtgärder för lagring.
- Repriser kan inträffa: Varje gång en funktion anropas med Event Hubs-utlösarbindningen använder den den senaste kontrollpunkten för att avgöra var bearbetningen ska återupptas. Förskjutningen för varje konsument sparas på partitionsnivå för varje konsumentgrupp. Repriser inträffar när en kontrollpunkt inte inträffar under funktionens senaste anrop och funktionen anropas igen. Mer information om dubbletter och dedupliceringstekniker finns i Idempotens.
Att förstå kontrollpunkter blir viktigt när du överväger metodtips för felhantering och återförsök, ett ämne som beskrivs senare i den här artikeln.
Telemetrisampling
Functions har inbyggt stöd för Application Insights, ett tillägg för Azure Monitor som tillhandahåller funktioner för övervakning av programprestanda. Med den här funktionen kan du logga information om funktionsaktiviteter, prestanda, körningsundundentag med mera. Mer information finns i Översikt över Application Insights.
Den här kraftfulla funktionen erbjuder några viktiga konfigurationsalternativ som påverkar prestanda. Några av de viktigaste inställningarna och övervägandena för övervakning och prestanda är:
- Aktivera telemetrisampling: För scenarier med högt dataflöde bör du utvärdera mängden telemetri och information som du behöver. Överväg att använda telemetrisamplingsfunktionen i Application Insights för att undvika att försämra funktionens prestanda med onödig telemetri och mått.
- Konfigurera sammansättningsinställningar: Granska och konfigurera frekvensen för att aggregera och skicka data till Application Insights. Den här konfigurationsinställningen finns i filen host.json tillsammans med många andra alternativ för sampling och loggning. Mer information finns i Konfigurera aggregatorn.
- Inaktivera AzureWebJobDashboard: För appar som är målversion 1.x av Functions-körningen lagrar den här inställningen anslutningssträng till ett lagringskonto som Azure SDK använder för att behålla loggar för instrumentpanelen webjobs. Om Application Insights används i stället för instrumentpanelen för webbjobb bör den här inställningen tas bort. Mer information finns i AzureWebJobsDashboard.
När Application Insights är aktiverat utan sampling skickas all telemetri. Att skicka data om alla händelser kan ha en skadlig effekt på funktionens prestanda, särskilt under scenarier med hög dataflödeshändelseströmning.
Att dra nytta av provtagningen och kontinuerligt utvärdera den lämpliga mängd telemetri som behövs för övervakning är avgörande för optimala prestanda. Telemetri ska användas för utvärdering av allmän plattformshälsa och för tillfällig felsökning, inte för att samla in viktiga affärsmått. Mer information finns i Konfigurera sampling.
Utdatabindning
Använd Event Hubs-utdatabindningen för Azure Functions för att förenkla publiceringen till en händelseström från en funktion. Fördelarna med att använda den här bindningen är:
- Resurshantering: Bindningen hanterar både klient- och anslutningslivscykler åt dig och minskar risken för problem som kan uppstå med portöverbelastning och hantering av anslutningspooler.
- Mindre kod: Bindningen abstraherar det underliggande SDK:et och minskar mängden kod som du behöver för att publicera händelser. Det hjälper dig att skriva kod som är enklare att skriva och underhålla.
- Batchbearbetning: För flera språk stöds batchbearbetning för att effektivt publicera till en händelseström. Batchbearbetning kan förbättra prestanda och effektivisera koden som skickar händelserna.
Vi rekommenderar starkt att du granskar listan över språk som Functions stöder och utvecklarguiderna för dessa språk. Avsnittet Bindningar för varje språk innehåller detaljerade exempel och dokumentation.
Batchbearbetning vid publicering av händelser
Om funktionen bara publicerar en enskild händelse är det vanligt att konfigurera bindningen för att returnera ett värde om funktionskörningen alltid slutar med en instruktion som skickar händelsen. Den här tekniken bör endast användas för synkrona funktioner som endast returnerar en händelse.
Batchbearbetning uppmuntras att förbättra prestanda när flera händelser skickas till en dataström. Med batchbearbetning kan bindningen publicera händelser på ett så effektivt sätt som möjligt.
Stöd för att använda utdatabindningen för att skicka flera händelser till Event Hubs finns i C#, Java, Python och JavaScript.
Mata ut flera händelser med inprocessmodellen (C#)
Använd typerna ICollector och IAsyncCollector när du skickar flera händelser från en funktion i C#.
- ICollector <T>. Metoden Add() kan användas i både synkrona och asynkrona funktioner. Den kör tilläggsåtgärden så snart den anropas.
- IAsyncCollector<T>. AddAsync()-metoden förbereder de händelser som ska publiceras till händelseströmmen. Om du skriver en asynkron funktion bör du använda IAsyncCollector för att bättre hantera publicerade händelser.
Exempel på hur du använder C# för att publicera enskilda och flera händelser finns i Azure Event Hubs-utdatabindning för Azure Functions.
Mata ut flera händelser med den isolerade arbetsmodellen (C#)
Beroende på functions runtime-versionen stöder modellen Isolerad arbetare olika typer för de parametrar som skickas till utdatabindningen. För flera händelser används en matris för att kapsla in uppsättningen. Vi rekommenderar att du granskar utdatabindningsattributen och användningsinformationen för den isolerade modellen och noterar skillnaderna mellan tilläggsversionerna.
Begränsning och bakåttryck
Begränsningsöverväganden gäller för utdatabindningar, inte bara för Event Hubs utan även för Azure-tjänster som Azure Cosmos DB. Det är viktigt att bekanta sig med de gränser och kvoter som gäller för dessa tjänster och att planera därefter.
Om du vill hantera underordnade fel med inprocessmodellen kan du omsluta AddAsync och FlushAsync i en undantagshanterare för .NET Functions för att fånga undantag från IAsyncCollector. Ett annat alternativ är att använda Event Hubs SDK:er direkt i stället för att använda utdatabindningar.
Om du använder den isolerade modellen för funktioner bör strukturerad undantagshantering användas för att på ett ansvarsfullt sätt fånga undantag när utdatavärdena returneras.
Funktionskod
Det här avsnittet beskriver de nyckelområden som måste beaktas när du skriver kod för att bearbeta händelser i en funktion som Event Hubs utlöser.
Asynkron programmering
Vi rekommenderar att du skriver din funktion för att använda asynkron kod och undvika blockeringsanrop, särskilt när I/O-anrop är inblandade.
Här följer riktlinjer som du bör följa när du skriver en funktion för att bearbeta asynkront:
- Alla asynkrona eller synkrona: Om en funktion är konfigurerad att köras asynkront bör alla I/O-anrop vara asynkrona. I de flesta fall är delvis asynkron kod sämre än kod som är helt synkron. Välj antingen asynkron eller synkron och håll dig till valet hela vägen.
- Undvik blockeringsanrop: Blockera anrop återgår endast till anroparen när samtalet har slutförts, till skillnad från asynkrona anrop som returneras omedelbart. Ett exempel i C# skulle vara att anropa Task.Result eller Task.Wait på en asynkron åtgärd.
Mer om att blockera anrop
Om du använder blockeringsanrop för asynkrona åtgärder kan det leda till utsvulten trådpool och leda till att funktionsprocessen kraschar. Kraschen inträffar eftersom ett blockeringsanrop kräver att en annan tråd skapas för att kompensera för det ursprungliga anropet som nu väntar. Det innebär att det krävs dubbelt så många trådar för att slutföra åtgärden.
Att undvika den här synkroniseringen via asynkron metod är särskilt viktigt när Event Hubs är inblandat, eftersom en funktionskrasch inte uppdaterar kontrollpunkten. Nästa gång funktionen anropas kan den hamna i den här cykeln och verkar ha fastnat eller gå långsamt när funktionskörningarna slutligen överskrider tidsgränsen.
Felsökning av det här fenomenet börjar vanligtvis med att granska utlösarinställningarna och köra experiment som kan innebära att öka antalet partitioner. Undersökningar kan också leda till att flera av batchalternativen ändras, till exempel den maximala batchstorleken eller antalet prefetch. Intrycket är att det är ett dataflödesproblem eller en konfigurationsinställning som bara behöver justeras i enlighet med detta. Huvudproblemet finns dock i själva koden och måste åtgärdas där.
Deltagare
Den här artikeln underhålls av Microsoft. Den skrevs ursprungligen av följande deltagare.
Huvudförfattare:
- David Barkol | Huvudlösningsspecialist GBB
Om du vill se icke-offentliga LinkedIn-profiler loggar du in på LinkedIn.
Nästa steg
Innan du fortsätter bör du överväga att granska dessa relaterade artiklar:
- Övervaka körningar i Azure Functions
- Tillförlitlig händelsebearbetning i Azure Functions
- Utforma Azure Functions för identiska indata
- ASP.NET asynkron vägledning för Core.
- Azure Event Hubs-utlösare för Azure Functions