Beperkingen voor Orchestrator-functiecode
Durable Functions is een uitbreiding van Azure Functions waarmee u stateful apps kunt bouwen. U kunt een orchestratorfunctie gebruiken om de uitvoering van andere duurzame functies in een functie-app in te delen. Orchestrator-functies zijn stateful, betrouwbaar en mogelijk langlopend.
Codebeperkingen voor orchestrator
Orchestrator-functies maken gebruik van gebeurtenisbronnen om een betrouwbare uitvoering te garanderen en de status van de lokale variabele te behouden. Het herhalingsgedrag van orchestratorcode creëert beperkingen voor het type code dat u in een orchestratorfunctie kunt schrijven. Orchestratorfuncties moeten bijvoorbeeld deterministisch zijn: een orchestratorfunctie wordt meerdere keren opnieuw afgespeeld en moet elke keer hetzelfde resultaat opleveren.
Deterministische API's gebruiken
Deze sectie bevat enkele eenvoudige richtlijnen die helpen ervoor te zorgen dat uw code deterministisch is.
Orchestrator-functies kunnen elke API in hun doeltalen aanroepen. Het is echter belangrijk dat orchestratorfuncties alleen deterministische API's aanroepen. Een deterministische API is een API die altijd dezelfde waarde retourneert op basis van dezelfde invoer, ongeacht wanneer of hoe vaak deze wordt aangeroepen.
De volgende secties bevatten richtlijnen voor API's en patronen die u moet vermijden omdat ze niet deterministisch zijn. Deze beperkingen zijn alleen van toepassing op orchestratorfuncties. Andere functietypen hebben dergelijke beperkingen niet.
Notitie
Hieronder worden verschillende typen codebeperkingen beschreven. Deze lijst is helaas niet volledig en sommige gebruiksvoorbeelden worden mogelijk niet behandeld. Het belangrijkste om te overwegen bij het schrijven van orchestratorcode is of een API die u gebruikt deterministisch is. Als u eenmaal vertrouwd bent met het denken op deze manier, is het gemakkelijk om te begrijpen welke API's veilig kunnen worden gebruikt en welke niet zonder dat u deze gedocumenteerde lijst hoeft te raadplegen.
Datums en tijden
API's die de huidige datum of tijd retourneren, zijn niet-deterministisch en mogen nooit worden gebruikt in orchestratorfuncties. Dit komt doordat elke orchestratorfunctie die opnieuw wordt afgespeeld, een andere waarde produceert. Gebruik in plaats daarvan de Durable Functions equivalente API voor het ophalen van de huidige datum of tijd, die consistent blijft voor alle herhalingen.
Gebruik DateTime.Now
geen , DateTime.UtcNow
of equivalente API's om de huidige tijd op te halen. Klassen zoals Stopwatch
moeten ook worden vermeden. Voor in-process orchestratorfuncties van .NET gebruikt u de IDurableOrchestrationContext.CurrentUtcDateTime
eigenschap om de huidige tijd op te halen. Voor .NET geïsoleerde orchestratorfuncties gebruikt u de TaskOrchestrationContext.CurrentDateTimeUtc
eigenschap om de huidige tijd op te halen.
DateTime startTime = context.CurrentUtcDateTime;
// do some work
TimeSpan totalTime = context.CurrentUtcDateTime.Subtract(startTime);
GUID's en UUID's
API's die een willekeurige GUID of UUID retourneren, zijn niet-deterministisch omdat de gegenereerde waarde voor elke herhaling anders is. Afhankelijk van de taal die u gebruikt, is er mogelijk een ingebouwde API beschikbaar voor het genereren van deterministische GUID's of UUID's. Gebruik anders een activiteitsfunctie om een willekeurig gegenereerde GUID of UUID te retourneren.
Gebruik geen API's zoals Guid.NewGuid()
om willekeurige GUID's te genereren. Gebruik in plaats daarvan de API van NewGuid()
het contextobject om een willekeurige GUID te genereren die veilig is voor het opnieuw afspelen van orchestrators.
Guid randomGuid = context.NewGuid();
Notitie
GUID's die zijn gegenereerd met indelingscontext-API's zijn Type 5-UUID's.
Willekeurige getallen
Gebruik een activiteitsfunctie om willekeurige getallen te retourneren naar een orchestratorfunctie. De retourwaarden van activiteitsfuncties zijn altijd veilig voor herhaling, omdat ze worden opgeslagen in de indelingsgeschiedenis.
Een willekeurige getalgenerator met een vaste seed-waarde kan ook rechtstreeks in een orchestratorfunctie worden gebruikt. Deze benadering is veilig zolang dezelfde reeks getallen wordt gegenereerd voor elke indelingsherhaling.
Bindingen
Een orchestratorfunctie mag geen bindingen gebruiken, ook niet de bindingen van de indelingsclient en de entiteitsclient . Gebruik altijd invoer- en uitvoerbindingen vanuit een client of activiteitsfunctie. Dit is belangrijk omdat orchestratorfuncties meerdere keren kunnen worden afgespeeld, waardoor niet-deterministische en dubbele I/O met externe systemen wordt veroorzaakt.
Statische variabelen
Vermijd het gebruik van statische variabelen in orchestratorfuncties, omdat hun waarden in de loop van de tijd kunnen veranderen, wat resulteert in niet-deterministisch runtimegedrag. Gebruik in plaats daarvan constanten of beperk het gebruik van statische variabelen tot activiteitsfuncties.
Notitie
Zelfs buiten orchestratorfuncties kan het gebruik van statische variabelen in Azure Functions om verschillende redenen problematisch zijn, omdat er geen garantie is dat de statische status behouden blijft bij meerdere functie-uitvoeringen. Statische variabelen moeten worden vermeden, behalve in zeer specifieke usecases, zoals de best-effort in cache opslaan in het geheugen in activiteits- of entiteitsfuncties.
Omgevingsvariabelen
Gebruik geen omgevingsvariabelen in orchestratorfuncties. Hun waarden kunnen na verloop van tijd veranderen, wat resulteert in niet-deterministisch runtimegedrag. Als een orchestratorfunctie configuratie nodig heeft die is gedefinieerd in een omgevingsvariabele, moet u de configuratiewaarde doorgeven aan de orchestratorfunctie als invoer of als de retourwaarde van een activiteitsfunctie.
Netwerk en HTTP
Gebruik activiteitsfuncties om uitgaande netwerkoproepen te doen. Als u een HTTP-aanroep moet doen vanuit uw orchestratorfunctie, kunt u ook de duurzame HTTP-API's gebruiken.
API's voor het blokkeren van threads
Het blokkeren van API's zoals 'slaapstand' kan prestatie- en schaalproblemen veroorzaken voor orchestratorfuncties en moet worden vermeden. In het Azure Functions Consumption-abonnement kunnen ze zelfs leiden tot onnodige uitvoeringstijdkosten. Gebruik alternatieven voor het blokkeren van API's wanneer deze beschikbaar zijn. Gebruik duurzame timers bijvoorbeeld om vertragingen te maken die veilig zijn voor opnieuw afspelen en die niet meetellen voor de uitvoeringstijd van een orchestratorfunctie.
Asynchrone API's
Orchestratorcode mag nooit asynchrone bewerkingen starten, behalve de bewerkingen die zijn gedefinieerd door het contextobject van de indelingstrigger. Gebruik Task.Run
bijvoorbeeld nooit , Task.Delay
en HttpClient.SendAsync
in .NET of setTimeout
en setInterval
in JavaScript. Een orchestratorfunctie mag alleen asynchroon werk plannen met behulp van Durable SDK-API's, zoals functies voor planningsactiviteiten. Elk ander type asynchrone aanroepen moet worden uitgevoerd binnen activiteitsfuncties.
Asynchrone JavaScript-functies
JavaScript-orchestratorfuncties altijd declareren als synchrone generatorfuncties. U moet JavaScript-orchestratorfuncties niet declareren als async
omdat de Node.js runtime niet garandeert dat asynchrone functies deterministisch zijn.
Python-coroutines
U moet python-orchestratorfuncties niet declareren als coroutines. Met andere woorden, declareer python-orchestratorfuncties nooit met het async
trefwoord omdat coroutine semantiek niet overeenkomt met het Durable Functions replay-model. U moet python-orchestratorfuncties altijd declareren als generatoren, wat betekent dat u moet verwachten dat de context
API wordt gebruikt yield
in plaats van await
.
.NET-threading-API's
Het Durable Task Framework voert orchestratorcode uit op één thread en kan niet communiceren met andere threads. Het uitvoeren van asynchrone vervolgbewerkingen op een werkrolgroepthread van een indeling kan leiden tot niet-deterministische uitvoering of impasses. Daarom moeten orchestratorfuncties bijna nooit threading-API's gebruiken. Gebruik ConfigureAwait(continueOnCapturedContext: false)
bijvoorbeeld nooit in een orchestratorfunctie. Dit zorgt ervoor dat taakvervolgingen worden uitgevoerd op de oorspronkelijke SynchronizationContext
orchestrator-functie .
Notitie
Het Durable Task Framework probeert onbedoeld gebruik van niet-orchestrator-threads in orchestrator-functies te detecteren. Als er een schending wordt gevonden, genereert het framework een uitzondering NonDeterministicOrchestrationException . Dit detectiegedrag ondervangt echter niet alle schendingen en u moet er niet van afhankelijk zijn.
Versiebeheer
Een duurzame indeling kan dagen, maanden, jaren of zelfs eeuwig worden uitgevoerd. Alle code-updates voor Durable Functions apps die van invloed zijn op onvoltooide indelingen, kunnen het gedrag van de replay van de orchestrations verstoren. Daarom is het belangrijk om zorgvuldig te plannen bij het aanbrengen van updates voor code. Zie het artikel over versiebeheer voor een meer gedetailleerde beschrijving van het versiebeheer van uw code.
Duurzame taken
Notitie
In deze sectie worden de interne implementatiedetails van het Durable Task Framework beschreven. U kunt duurzame functies gebruiken zonder deze informatie te kennen. Het is alleen bedoeld om u inzicht te geven in het gedrag van opnieuw afspelen.
Taken die veilig kunnen wachten in orchestratorfuncties, worden soms duurzame taken genoemd. Het Durable Task Framework maakt en beheert deze taken. Voorbeelden zijn de taken die worden geretourneerd door CallActivityAsync
, WaitForExternalEvent
en CreateTimer
in .NET-orchestratorfuncties.
Deze duurzame taken worden intern beheerd door een lijst TaskCompletionSource
met objecten in .NET. Tijdens het opnieuw afspelen worden deze taken gemaakt als onderdeel van de uitvoering van orchestrator-code. Ze worden voltooid wanneer de dispatcher de bijbehorende geschiedenis-gebeurtenissen opsomt.
De taken worden synchroon uitgevoerd met behulp van één thread totdat de geschiedenis opnieuw is afgespeeld. Voor duurzame taken die niet zijn voltooid aan het einde van de geschiedenisherhaling, worden de juiste acties uitgevoerd. Een bericht kan bijvoorbeeld worden gebeld om een activiteitsfunctie aan te roepen.
De beschrijving van het runtime-gedrag in deze sectie moet u helpen begrijpen waarom een orchestrator-functie niet kan worden gebruikt await
in of yield
in een niet-belastbare taak. Er zijn twee redenen: de dispatcher-thread kan niet wachten tot de taak is voltooid en een callback door die taak kan de traceringsstatus van de orchestratorfunctie mogelijk beschadigen. Er zijn enkele runtimecontroles uitgevoerd om deze schendingen te detecteren.
Raadpleeg de broncode van Durable Task op GitHub voor meer informatie over hoe het Durable Task Framework orchestrator-functies uitvoert. Zie met name TaskOrchestrationExecutor.cs en TaskOrchestrationContext.cs.