Azure Functions betrouwbare gebeurtenisverwerking

Gebeurtenisverwerking is een van de meest voorkomende scenario's die zijn gekoppeld aan serverloze architectuur. In dit artikel wordt beschreven hoe u een betrouwbare berichtenprocessor maakt met Azure Functions om te voorkomen dat berichten verloren gaan.

Uitdagingen van gebeurtenisstromen in gedistribueerde systemen

Overweeg een systeem dat gebeurtenissen verzendt met een constante snelheid van 100 gebeurtenissen per seconde. Met deze snelheid kunnen binnen enkele minuten meerdere parallelle Functions-exemplaren de binnenkomende 100 gebeurtenissen per seconde verbruiken.

Een van de volgende minder optimale omstandigheden is echter mogelijk:

  • Wat gebeurt er als de uitgever van de gebeurtenis een beschadigde gebeurtenis verzendt?
  • Wat gebeurt er als uw Functions-exemplaar niet-verwerkte uitzonderingen tegenkomt?
  • Wat gebeurt er als een downstreamsysteem offline gaat?

Hoe gaat u om met deze situaties terwijl de doorvoer van uw toepassing behouden blijft?

Met wachtrijen is betrouwbare berichten vanzelfsprekend. Wanneer de functie wordt gekoppeld aan een Functions-trigger, wordt het wachtrijbericht vergrendeld. Als de verwerking mislukt, wordt de vergrendeling vrijgegeven zodat een ander exemplaar de verwerking opnieuw kan proberen. De verwerking gaat vervolgens door totdat het bericht is geëvalueerd of wordt toegevoegd aan een gifwachtrij.

Zelfs als een enkel wachtrijbericht in een cyclus voor opnieuw proberen kan blijven, blijven andere parallelle uitvoeringen de resterende berichten uit de wachtrij verwijderen. Het resultaat is dat de algehele doorvoer grotendeels niet wordt beïnvloed door één slecht bericht. Opslagwachtrijen bieden echter geen garantie voor de volgorde en zijn niet geoptimaliseerd voor de hoge doorvoervereisten die door Event Hubs worden vereist.

Azure Event Hubs bevat daarentegen geen vergrendelingsconcept. Event Hubs-gebeurtenissen gedragen zich meer als een videospeler om functies als hoge doorvoer, meerdere consumentengroepen en mogelijkheden voor opnieuw afspelen mogelijk te maken. Gebeurtenissen worden gelezen vanaf één punt in de stream per partitie. Vanaf de aanwijzer kunt u vooruit- of achteruit lezen vanaf die locatie, maar u moet ervoor kiezen om de aanwijzer te verplaatsen om gebeurtenissen te verwerken.

Wanneer er fouten optreden in een stream en u besluit de aanwijzer op dezelfde plek te houden, wordt de gebeurtenisverwerking geblokkeerd totdat de aanwijzer is geavanceerd. Met andere woorden, als de aanwijzer wordt gestopt om problemen met het verwerken van één gebeurtenis af te handelen, beginnen de niet-verwerkte gebeurtenissen zich op te stapelen.

Azure Functions voorkomt impasses door de aanwijzer van de stroom vooruit te gaan, ongeacht of het lukt of mislukt. Omdat de aanwijzer steeds verder gaat, moeten uw functies fouten op de juiste manier verwerken.

Hoe Azure Functions Event Hubs-gebeurtenissen verbruikt

Azure Functions gebruikt Event Hub-gebeurtenissen terwijl u de volgende stappen doorloopt:

  1. Er wordt een aanwijzer gemaakt en opgeslagen in Azure Storage voor elke partitie van de Event Hub.
  2. Wanneer er nieuwe berichten worden ontvangen (standaard in een batch), probeert de host de functie te activeren met de batch berichten.
  3. Als de uitvoering van de functie is voltooid (met of zonder uitzondering), gaat de aanwijzer verder en wordt een controlepunt opgeslagen in het opslagaccount.
  4. Als de uitvoering van de functie niet kan worden voltooid door omstandigheden, kan de host geen voortgang maken met de aanwijzer. Als de aanwijzer niet geavanceerd is, worden bij latere controles dezelfde berichten verwerkt.
  5. Herhaal de stappen 2-4

Dit gedrag onthult een aantal belangrijke punten:

  • Niet-verwerkte uitzonderingen kunnen ertoe leiden dat u berichten kwijtraakt. Uitvoeringen die resulteren in een uitzondering, blijven de aanwijzer doorlopen. Als u beleid voor opnieuw proberen instelt, wordt de voortgang van de aanwijzer vertraagd totdat het hele beleid voor opnieuw proberen is geëvalueerd.
  • Functions garandeert at-least-once levering. Uw code en afhankelijke systemen moeten mogelijk rekening houden met het feit dat hetzelfde bericht twee keer kan worden ontvangen.

Afhandeling van uitzonderingen

Over het algemeen moet elke functie een try/catch-blok bevatten op het hoogste codeniveau. Met name alle functies die Event Hubs-gebeurtenissen gebruiken, moeten een catch blok hebben. Op die manier, wanneer een uitzondering wordt gegenereerd, verwerkt het catch-blok de fout voordat de aanwijzer wordt voortgezet.

Mechanismen en beleid voor opnieuw proberen

Sommige uitzonderingen zijn tijdelijk van aard en worden niet opnieuw weergegeven wanneer een bewerking even later opnieuw wordt geprobeerd. Daarom bestaat de eerste stap altijd uit het opnieuw proberen van de bewerking. U kunt gebruikmaken van het beleid voor opnieuw proberen van de functie-app of de ontwerplogica voor opnieuw proberen binnen de uitvoering van de functie.

Door foutafhandelingsgedrag in uw functies te introduceren, kunt u zowel basisbeleid als geavanceerde beleidsregels voor opnieuw proberen definiëren. U kunt bijvoorbeeld een beleid implementeren dat volgt op een werkstroom die wordt geïllustreerd door de volgende regels:

  • Probeer drie keer een bericht in te voegen (mogelijk met een vertraging tussen nieuwe pogingen).
  • Als het uiteindelijke resultaat van alle nieuwe pogingen een fout is, voegt u een bericht toe aan een wachtrij, zodat de verwerking kan worden voortgezet in de stream.
  • Beschadigde of niet-verwerkte berichten worden later verwerkt.

Notitie

Polly is een voorbeeld van een bibliotheek voor tolerantie en tijdelijke foutafhandeling voor C#-toepassingen.

Niet-uitzonderingsfouten

Sommige problemen doen zich voor, zelfs wanneer een fout niet aanwezig is. Denk bijvoorbeeld aan een fout die midden in een uitvoering optreedt. Als in dit geval de uitvoering van een functie niet wordt voltooid, wordt de offsetpointer nooit voortgezet. Als de aanwijzer niet verder gaat, blijft elk exemplaar dat wordt uitgevoerd na een mislukte uitvoering dezelfde berichten lezen. Deze situatie biedt een "at-least-once" garantie.

De zekerheid dat elk bericht ten minste één keer wordt verwerkt, impliceert dat sommige berichten meer dan één keer kunnen worden verwerkt. Uw functie-apps moeten zich bewust zijn van deze mogelijkheid en moeten worden gebouwd rond de principes van idempotentie.

Uitvoering stoppen en opnieuw starten

Hoewel een paar fouten acceptabel kunnen zijn, wat als uw app aanzienlijke fouten ondervindt? Mogelijk wilt u stoppen met het activeren van gebeurtenissen totdat het systeem de status in orde heeft bereikt. De mogelijkheid om de verwerking te onderbreken, wordt vaak bereikt met een circuitonderbrekerpatroon. Met het circuitonderbrekerpatroon kan uw app het circuit van het gebeurtenisproces 'onderbreken' en op een later tijdstip hervatten.

Er zijn twee onderdelen nodig om een circuitonderbreker te implementeren in een gebeurtenisproces:

  • Gedeelde status voor alle exemplaren om de status van het circuit bij te houden en te bewaken
  • Masterproces waarmee de status van het circuit kan worden beheerd (open of gesloten)

Implementatiedetails kunnen variëren, maar als u de status wilt delen tussen exemplaren, hebt u een opslagmechanisme nodig. U kunt ervoor kiezen om de status op te slaan in Azure Storage, een Redis-cache of een ander account dat toegankelijk is via een verzameling functies.

Azure Logic Apps of durable functions zijn een natuurlijke oplossing voor het beheren van de werkstroom- en circuitstatus. Andere services werken mogelijk net zo goed, maar voor dit voorbeeld worden logische apps gebruikt. Met behulp van logische apps kunt u de uitvoering van een functie onderbreken en opnieuw starten, zodat u de controle hebt die nodig is om het circuitonderbrekerpatroon te implementeren.

Een drempelwaarde voor fouten definiëren voor verschillende exemplaren

Als u rekening wilt houden met meerdere exemplaren die gelijktijdig gebeurtenissen verwerken, moet de gedeelde externe status behouden om de status van het circuit te bewaken.

Een regel die u kunt implementeren, kan het volgende afdwingen:

  • Als er meer dan 100 uiteindelijke fouten zijn binnen 30 seconden voor alle exemplaren, onderbreekt u het circuit en stopt u met het activeren van nieuwe berichten.

De implementatiedetails variëren afhankelijk van uw behoeften, maar over het algemeen kunt u een systeem maken dat:

  1. Fouten vastleggen in een opslagaccount (Azure Storage, Redis, enzovoort)
  2. Wanneer nieuwe fouten worden geregistreerd, controleert u het rolling count om te zien of de drempelwaarde is bereikt (bijvoorbeeld meer dan 100 in de afgelopen 30 seconden).
  3. Als de drempelwaarde wordt bereikt, verzendt u een gebeurtenis naar Azure Event Grid vertelt u het systeem dat het circuit moet worden verbroken.

Circuitstatus beheren met Azure Logic Apps

In de volgende beschrijving wordt één manier beschreven waarop u een logische Azure-app kunt maken om de verwerking van een Functions-app te stoppen.

Azure Logic Apps wordt geleverd met ingebouwde connectors voor verschillende services, beschikt over stateful indelingen en is een natuurlijke keuze voor het beheren van de circuitstatus. Nadat is gedetecteerd dat het circuit moet worden onderbroken, kunt u een logische app bouwen om de volgende werkstroom te implementeren:

  1. Een Event Grid-werkstroom activeren en de Azure-functie stoppen (met de Azure Resource-connector)
  2. Een e-mailmelding verzenden met een optie om de werkstroom opnieuw te starten

De ontvanger van het e-mailbericht kan de status van het circuit onderzoeken en, indien van toepassing, het circuit opnieuw starten via een koppeling in de e-mailmelding. Wanneer de functie opnieuw wordt gestart door de werkstroom, worden berichten verwerkt vanaf het laatste Event Hub-controlepunt.

Op deze manier gaan er geen berichten verloren, worden alle berichten op volgorde verwerkt en kunt u het circuit zo lang als nodig onderbreken.

Resources

Volgende stappen

Zie de volgende resources voor meer informatie: