Dela via


Begränsningar för Orchestrator-funktionskod

Durable Functions är ett tillägg till Azure Functions som gör att du kan skapa tillståndskänsliga appar. Du kan använda en orkestreringsfunktion för att samordna körningen av andra varaktiga funktioner i en funktionsapp. Orchestrator-funktioner är tillståndskänsliga, tillförlitliga och potentiellt tidskrävande.

Begränsningar för orkestrerarkod

Orchestrator-funktioner använder händelsekällor för att säkerställa tillförlitlig körning och för att upprätthålla lokalt variabeltillstånd. Uppspelningsbeteendet för orchestrator-kod skapar begränsningar för den typ av kod som du kan skriva i en orchestrator-funktion. Till exempel måste orkestreringsfunktioner vara deterministiska: en orkestreringsfunktion spelas upp flera gånger och måste generera samma resultat varje gång.

Använda deterministiska API:er

Det här avsnittet innehåller några enkla riktlinjer som hjälper dig att se till att koden är deterministisk.

Orchestrator-funktioner kan anropa valfritt API på sina målspråk. Det är dock viktigt att orchestrator-funktioner endast anropar deterministiska API:er. Ett deterministiskt API är ett API som alltid returnerar samma värde med samma indata, oavsett när eller hur ofta det anropas.

Följande avsnitt innehåller vägledning om API:er och mönster som du bör undvika eftersom de inte är deterministiska. Dessa begränsningar gäller endast för orkestreringsfunktioner. Andra funktionstyper har inte sådana begränsningar.

Anteckning

Flera typer av kodbegränsningar beskrivs nedan. Den här listan är tyvärr inte omfattande och vissa användningsfall kanske inte omfattas. Det viktigaste du bör tänka på när du skriver orchestrator-kod är om ett API som du använder är deterministiskt. När du är bekväm med att tänka på det här sättet är det lätt att förstå vilka API:er som är säkra att använda och som inte är utan att behöva referera till den här dokumenterade listan.

Datum och tider

API:er som returnerar aktuellt datum eller aktuell tid är icke-deterministiska och bör aldrig användas i orchestrator-funktioner. Det beror på att varje orchestrator-funktionsrepris ger ett annat värde. Du bör i stället använda Durable Functions motsvarande API för att hämta aktuellt datum eller tid, vilket förblir konsekvent över repriser.

Använd DateTime.Nowinte , DateTime.UtcNoweller motsvarande API:er för att hämta den aktuella tiden. Klasser som Stopwatch bör också undvikas. För .NET-processerkestreringsfunktioner använder du IDurableOrchestrationContext.CurrentUtcDateTime egenskapen för att hämta aktuell tid. För .NET isolerade orkestreringsfunktioner använder du TaskOrchestrationContext.CurrentDateTimeUtc egenskapen för att hämta den aktuella tiden.

DateTime startTime = context.CurrentUtcDateTime;
// do some work
TimeSpan totalTime = context.CurrentUtcDateTime.Subtract(startTime);

GUID och UUID

API:er som returnerar ett slumpmässigt GUID eller UUID är icke-deterministiska eftersom det genererade värdet skiljer sig åt för varje återuppspelning. Beroende på vilket språk du använder kan ett inbyggt API för att generera deterministiska GUID:er eller UUID:er vara tillgängligt. Annars använder du en aktivitetsfunktion för att returnera ett slumpmässigt genererat GUID eller UUID.

Använd inte API:er som Guid.NewGuid() för att generera slumpmässiga GUID. Använd i stället kontextobjektets NewGuid() API för att generera ett slumpmässigt GUID som är säkert för orchestrator replay.

Guid randomGuid = context.NewGuid();

Anteckning

GUID:er som genereras med API:er för orkestreringskontext är UUID-typ 5.

Slumptal

Använd en aktivitetsfunktion för att returnera slumpmässiga tal till en orkestreringsfunktion. Returvärdena för aktivitetsfunktioner är alltid säkra för uppspelning eftersom de sparas i orkestreringshistoriken.

Du kan också använda en slumptalsgenerator med ett fast startvärde direkt i en orkestreringsfunktion. Den här metoden är säker så länge samma sekvens med tal genereras för varje orkestreringsrepris.

Bindningar

En orkestreringsfunktion får inte använda några bindningar, inklusive inte ens orkestreringsklient - och entitetsklientbindningar . Använd alltid indata- och utdatabindningar inifrån en klient eller aktivitetsfunktion. Detta är viktigt eftersom orkestreringsfunktioner kan spelas upp flera gånger, vilket orsakar icke-deterministisk och duplicerad I/O med externa system.

Statiska variabler

Undvik att använda statiska variabler i orchestrator-funktioner eftersom deras värden kan ändras över tid, vilket resulterar i icke-deterministiskt körningsbeteende. Använd i stället konstanter eller begränsa användningen av statiska variabler till aktivitetsfunktioner.

Anteckning

Även utanför orkestreringsfunktioner kan det vara problematiskt att använda statiska variabler i Azure Functions av olika orsaker eftersom det inte finns någon garanti för att statiskt tillstånd bevaras över flera funktionskörningar. Statiska variabler bör undvikas förutom i mycket specifika användningsfall, till exempel cachelagring i minnet i aktivitets- eller entitetsfunktioner.

Miljövariabler

Använd inte miljövariabler i orchestrator-funktioner. Deras värden kan ändras med tiden, vilket resulterar i icke-deterministiskt körningsbeteende. Om en orkestreringsfunktion behöver konfiguration som har definierats i en miljövariabel måste du skicka konfigurationsvärdet till orchestrator-funktionen som indata eller som returvärde för en aktivitetsfunktion.

Nätverk och HTTP

Använd aktivitetsfunktioner för att göra utgående nätverksanrop. Om du behöver göra ett HTTP-anrop från orkestreringsfunktionen kan du också använda hållbara HTTP-API:er.

Trådblockerande API:er

Blockering av API:er som "viloläge" kan orsaka prestanda- och skalningsproblem för orkestreringsfunktioner och bör undvikas. I den Azure Functions förbrukningsplanen kan de till och med resultera i onödiga avgifter för körningstid. Använd alternativ till att blockera API:er när de är tillgängliga. Du kan till exempel använda varaktiga timers för att skapa fördröjningar som är säkra för uppspelning och som inte räknas mot körningstiden för en orkestreringsfunktion.

Asynkrona API:er

Orchestrator-koden får aldrig starta någon asynkron åtgärd förutom de som definieras av orkestreringsutlösarens kontextobjekt. Använd till exempel aldrig Task.Run, Task.Delayoch HttpClient.SendAsync i .NET eller setTimeout och setInterval i JavaScript. En orkestreringsfunktion bör endast schemalägga asynkront arbete med hjälp av DURABLE SDK-API:er, till exempel schemaläggning av aktivitetsfunktioner. Alla andra typer av asynkrona anrop bör göras i aktivitetsfunktioner.

Async JavaScript-funktioner

Deklarera alltid JavaScript-orkestreringsfunktioner som synkrona generatorfunktioner. Du får inte deklarera JavaScript-orkestreringsfunktioner eftersom async Node.js runtime inte garanterar att asynkrona funktioner är deterministiska.

Python-coroutines

Du får inte deklarera Python Orchestrator-funktioner som coroutines. Deklarera med andra ord aldrig Python Orchestrator-funktioner med nyckelordet async eftersom coroutine-semantik inte överensstämmer med Durable Functions replay-modellen. Du måste alltid deklarera Python Orchestrator-funktioner som generatorer, vilket innebär att du bör förvänta dig att API:et context används yield i stället för await.

.NET-trådnings-API:er

Durable Task Framework kör orchestrator-kod på en enda tråd och kan inte interagera med andra trådar. Om du kör asynkrona fortsättningar på en arbetspooltråd kan en orkestreringskörning resultera i icke-obestämd körning eller dödlägen. Därför bör orchestrator-funktioner nästan aldrig använda trådnings-API:er. Använd till exempel aldrig ConfigureAwait(continueOnCapturedContext: false) i en orchestrator-funktion. Detta säkerställer att aktivitetsfortsättningar körs på orkestreringsfunktionens ursprungliga SynchronizationContext.

Anteckning

Durable Task Framework försöker identifiera oavsiktlig användning av icke-orkestreringstrådar i orchestrator-funktioner. Om den hittar en överträdelse utlöser ramverket ett undantagsfel av typen NonDeterministicOrchestrationException . Det här identifieringsbeteendet fångar dock inte upp alla överträdelser och du bör inte vara beroende av det.

Versionshantering

En varaktig orkestrering kan köras kontinuerligt i dagar, månader, år eller till och med för evigt. Alla koduppdateringar som görs för Durable Functions appar som påverkar oavslutade orkestreringar kan bryta orkestreringarnas uppspelningsbeteende. Därför är det viktigt att planera noggrant när du gör uppdateringar av kod. En mer detaljerad beskrivning av hur du versionerar koden finns i versionsartikeln.

Varaktiga uppgifter

Anteckning

I det här avsnittet beskrivs intern implementeringsinformation för Durable Task Framework. Du kan använda varaktiga funktioner utan att känna till den här informationen. Den är endast avsedd att hjälpa dig att förstå reprisbeteendet.

Uppgifter som säkert kan vänta i orchestrator-funktioner kallas ibland för varaktiga uppgifter. Durable Task Framework skapar och hanterar dessa uppgifter. Exempel är de uppgifter som returneras av CallActivityAsync, WaitForExternalEventoch CreateTimer i .NET Orchestrator-funktioner.

Dessa varaktiga uppgifter hanteras internt av en lista över TaskCompletionSource objekt i .NET. Under reprisen skapas dessa uppgifter som en del av orchestrator-kodkörningen. De är klara när dispatcher räknar upp motsvarande historikhändelser.

Uppgifterna körs synkront med en enda tråd tills all historik har spelats upp igen. Varaktiga uppgifter som inte är slutförda i slutet av historikuppspelningen har lämpliga åtgärder utförda. Ett meddelande kan till exempel ställas in för att anropa en aktivitetsfunktion.

Det här avsnittets beskrivning av körningsbeteendet bör hjälpa dig att förstå varför en orchestrator-funktion inte kan använda await eller yield i en uppgift som inte kan användas. Det finns två orsaker: dispatcher-tråden kan inte vänta tills uppgiften har slutförts, och eventuella återanrop från den uppgiften kan skada spårningstillståndet för orchestrator-funktionen. Vissa körningskontroller finns på plats för att identifiera dessa överträdelser.

Mer information om hur Durable Task Framework kör orkestreringsfunktioner finns i källkoden för Durable Task på GitHub. I synnerhet, se TaskOrchestrationExecutor.cs och TaskOrchestrationContext.cs.

Nästa steg