Azure Functions tillförlitlig händelsebearbetning

Händelsebearbetning är ett av de vanligaste scenarierna som är associerade med serverlös arkitektur. Den här artikeln beskriver hur du skapar en tillförlitlig meddelandeprocessor med Azure Functions för att undvika att förlora meddelanden.

Utmaningar med händelseströmmar i distribuerade system

Överväg ett system som skickar händelser med en konstant hastighet på 100 händelser per sekund. I den här takten kan flera parallella Functions-instanser använda inkommande 100 händelser varje sekund inom några minuter.

Något av följande mindre optimala villkor är dock möjligt:

  • Vad händer om händelseutgivaren skickar en skadad händelse?
  • Vad händer om din Functions-instans stöter på ohanterade undantag?
  • Vad händer om ett nedströmssystem kopplas från?

Hur hanterar du dessa situationer samtidigt som du bevarar dataflödet för ditt program?

Med köer är tillförlitliga meddelanden naturligt. När funktionen parkopplas med en Functions-utlösare skapar den ett lås på kömeddelandet. Om bearbetningen misslyckas frigörs låset så att en annan instans kan försöka bearbeta igen. Bearbetningen fortsätter sedan tills meddelandet utvärderas korrekt eller läggs till i en giftkö.

Även om ett meddelande i en kö kan finnas kvar i en återförsökscykel fortsätter andra parallella körningar att fortsätta att rensa återstående meddelanden. Resultatet är att det övergripande dataflödet i stort sett inte påverkas av ett dåligt meddelande. Lagringsköer garanterar dock inte beställning och är inte optimerade för de höga dataflödeskrav som krävs av Event Hubs.

Däremot innehåller Azure Event Hubs inte ett låsningskoncept. Event Hubs-händelser fungerar mer som en videospelare för att tillåta funktioner som högt dataflöde, flera konsumentgrupper och uppspelningsfunktioner. Händelser läse från en enda punkt i dataströmmen per partition. Från pekaren kan du läsa framåt eller bakåt från den platsen, men du måste välja att flytta pekaren för händelser att bearbeta.

Om du bestämmer dig för att behålla pekaren på samma plats blockeras händelsebearbetningen tills pekaren har avancerats när fel inträffar i en ström. Med andra ord, om pekaren stoppas för att hantera problem med att bearbeta en enskild händelse, börjar de obearbetade händelserna staplas upp.

Azure Functions undviker dödlägen genom att flytta fram dataströmmens pekare oavsett om den lyckas eller misslyckas. Eftersom pekaren fortsätter att utvecklas måste dina funktioner hantera fel på rätt sätt.

Så här använder Azure Functions Event Hubs-händelser

Azure Functions använder Event Hub-händelser när du cyklar genom följande steg:

  1. En pekare skapas och sparas i Azure Storage för varje partition i händelsehubben.
  2. När nya meddelanden tas emot (i en batch som standard) försöker värden utlösa funktionen med batchen med meddelanden.
  3. Om funktionen slutför körningen (med eller utan undantag) avancerar pekaren och en kontrollpunkt sparas i lagringskontot.
  4. Om villkor förhindrar att funktionskörningen slutförs fortsätter inte värden med pekaren. Om pekaren inte är avancerad kommer senare kontroller att bearbeta samma meddelanden.
  5. Upprepa steg 2–4

Det här beteendet visar några viktiga punkter:

Hantering av undantag

Som en allmän regel bör varje funktion innehålla ett try/catch-block på den högsta kodnivån. Mer specifikt bör alla funktioner som använder Event Hubs-händelser ha ett catch block. På så sätt, när ett undantag utlöses, hanterar catch-blocket felet innan pekaren fortskrider.

Återförsöksmekanismer och principer

Vissa undantag är tillfälliga till sin natur och visas inte igen när en åtgärd försöker igen en stund senare. Det är därför det första steget alltid är att försöka utföra åtgärden igen. Du kan använda återförsöksprinciperna för funktionsappen eller logiken för omförsök av författare i funktionskörningen.

Genom att introducera felhanteringsbeteenden i dina funktioner kan du definiera både grundläggande och avancerade återförsöksprinciper. Du kan till exempel implementera en princip som följer ett arbetsflöde som illustreras av följande regler:

  • Försök att infoga ett meddelande tre gånger (eventuellt med en fördröjning mellan återförsöken).
  • Om det slutliga resultatet av alla återförsök är ett fel lägger du till ett meddelande i en kö så att bearbetningen kan fortsätta i dataströmmen.
  • Skadade eller obearbetade meddelanden hanteras sedan senare.

Anteckning

Polly är ett exempel på ett bibliotek för motståndskraft och hantering av tillfälliga fel för C#-program.

Fel som inte är undantag

Vissa problem uppstår även när ett fel inte finns. Tänk dig till exempel ett fel som inträffar mitt i en körning. I det här fallet, om en funktion inte slutför körningen, förloppsförskjutningspekaren aldrig. Om pekaren inte avancerar fortsätter alla instanser som körs efter en misslyckad körning att läsa samma meddelanden. Den här situationen ger en "minst en gång"-garanti.

Försäkran om att varje meddelande bearbetas minst en gång innebär att vissa meddelanden kan bearbetas mer än en gång. Dina funktionsappar måste vara medvetna om den här möjligheten och måste byggas kring principerna för idempotens.

Stoppa och starta om körningen

Även om några fel kan vara godtagbara, vad händer om din app upplever betydande fel? Du kanske vill sluta utlösa händelser tills systemet når ett felfritt tillstånd. Att ha möjlighet att pausa bearbetningen uppnås ofta med ett kretsbrytarmönster. Kretsbrytarmönstret gör att din app kan "bryta kretsen" för händelseprocessen och återupptas vid ett senare tillfälle.

Det krävs två delar för att implementera en kretsbrytare i en händelseprocess:

  • Delat tillstånd för alla instanser för att spåra och övervaka kretsens hälsotillstånd
  • Huvudprocess som kan hantera kretstillståndet (öppen eller stängd)

Implementeringsinformationen kan variera, men för att dela tillstånd mellan instanser behöver du en lagringsmekanism. Du kan välja att lagra tillstånd i Azure Storage, en Redis-cache eller något annat konto som kan nås av en samling funktioner.

Azure Logic Apps eller varaktiga funktioner är en naturlig plats för att hantera arbetsflödet och kretstillståndet. Andra tjänster kan fungera lika bra, men logikappar används för det här exemplet. Med hjälp av logikappar kan du pausa och starta om en funktions körning så att du får den kontroll som krävs för att implementera kretsbrytarmönstret.

Definiera ett tröskelvärde för fel mellan instanser

För att kunna ta hänsyn till flera instanser som bearbetar händelser samtidigt krävs bestående externt tillstånd för att övervaka kretsens hälsa.

En regel som du kan välja att implementera kan framtvinga att:

  • Om det uppstår fler än 100 eventuella fel inom 30 sekunder i alla instanser, bryter du kretsen och slutar utlösa nya meddelanden.

Implementeringsinformationen varierar beroende på dina behov, men i allmänhet kan du skapa ett system som:

  1. Loggfel på ett lagringskonto (Azure Storage, Redis osv.)
  2. När nya fel loggas kontrollerar du det löpande antalet för att se om tröskelvärdet är uppfyllt (till exempel mer än 100 under de senaste 30 sekunderna).
  3. Om tröskelvärdet uppfylls genererar du en händelse för att Azure Event Grid som uppmanar systemet att bryta kretsen.

Hantera kretstillstånd med Azure Logic Apps

Följande beskrivning visar ett sätt att skapa en Azure Logic App för att stoppa en Functions-app från bearbetning.

Azure Logic Apps levereras med inbyggda anslutningsappar till olika tjänster, funktioner tillståndskänsliga orkestreringar och är ett naturligt val för att hantera kretstillstånd. När du har upptäckt att kretsen måste brytas kan du skapa en logikapp för att implementera följande arbetsflöde:

  1. Utlösa ett Event Grid-arbetsflöde och stoppa Azure-funktionen (med Azure Resource Connector)
  2. Skicka ett e-postmeddelande som innehåller ett alternativ för att starta om arbetsflödet

E-postmottagaren kan undersöka kretsens hälsotillstånd och, i förekommande fall, starta om kretsen via en länk i e-postmeddelandet. När arbetsflödet startar om funktionen bearbetas meddelanden från den senaste händelsehubbens kontrollpunkt.

Med den här metoden går inga meddelanden förlorade, alla meddelanden bearbetas i ordning och du kan bryta kretsen så länge det behövs.

Resurser

Nästa steg

Mer information finns i följande resurser: