Varaktig orkestrering
Durable Functions är ett tillägg till Azure Functions. Du kan använda en orchestrator-funktion för att samordna körningen av andra Durable-funktioner i en funktionsapp. Orchestrator-funktioner har följande egenskaper:
- Orchestrator-funktioner definierar funktionsarbetsflöden med hjälp av procedurkod. Inga deklarativa scheman eller designers behövs.
- Orchestrator-funktioner kan anropa andra varaktiga funktioner synkront och asynkront. Utdata från anropade funktioner kan sparas på ett tillförlitligt sätt i lokala variabler.
- Orchestrator-funktioner är hållbara och tillförlitliga. Körningsstatusen kontrollpunkteras automatiskt när funktionen "väntar" eller "ger". Det lokala tillståndet förloras aldrig när processen återanvänds eller den virtuella datorn startas om.
- Orchestrator-funktioner kan vara tidskrävande. Den totala livslängden för en orkestreringsinstans kan vara sekunder, dagar, månader eller aldrig slut.
Den här artikeln ger dig en översikt över orkestreringsfunktioner och hur de kan hjälpa dig att lösa olika apputvecklingsutmaningar. Om du inte redan är bekant med de typer av funktioner som är tillgängliga i en Durable Functions-app läser du artikeln Durable Function types först.
Orkestreringsidentitet
Varje instans av en orkestrering har en instansidentifierare (även kallat instans-ID). Som standard är varje instans-ID ett automatiskt genererat GUID. Instans-ID:t kan dock också vara valfritt användargenererat strängvärde. Varje orkestreringsinstans-ID måste vara unikt i en aktivitetshubb.
Följande är några regler om instans-ID:t:
- Instans-ID:t måste vara mellan 1 och 100 tecken.
- Instans-ID:t får inte börja med
@
. - Instans-ID får inte innehålla
/
,\
,#
eller?
tecken. - Instans-ID:t får inte innehålla kontrolltecken.
Kommentar
Vi rekommenderar vanligtvis att du använder autogenererade instans-ID:t när det är möjligt. Användargenererade instans-ID:n är avsedda för scenarier där det finns en en-till-en-mappning mellan en orkestreringsinstans och en extern programspecifik entitet, till exempel en inköpsorder eller ett dokument.
Dessutom kan den faktiska tillämpningen av reglerna för teckenbegränsning variera beroende på vilken lagringsprovider som används av appen. För att säkerställa korrekt beteende och kompatibilitet rekommenderar vi starkt att du följer instans-ID-reglerna som angavs tidigare.
En orkestrerings instans-ID är en obligatorisk parameter för de flesta instanshanteringsåtgärder. De är också viktiga för diagnostik, till exempel genom att söka igenom orkestreringsspårningsdata i Application Insights i felsöknings- eller analyssyfte. Därför rekommenderar vi att du sparar genererade instans-ID:t på någon extern plats (till exempel en databas eller i programloggar) där de enkelt kan refereras senare.
Tillförlitlighet
Orchestrator-funktioner underhåller på ett tillförlitligt sätt sitt körningstillstånd med hjälp av designmönstret för händelsekällor . I stället för att direkt lagra det aktuella tillståndet för en orkestrering använder Durable Task Framework ett tilläggsarkiv för att registrera hela serien med åtgärder som funktionsorkestreringen vidtar. Ett tilläggslager har många fördelar jämfört med att "dumpa" hela körningstillståndet. Fördelarna är ökad prestanda, skalbarhet och svarstider. Du får också slutlig konsekvens för transaktionsdata och fullständiga spårningsspår och historik. Granskningsspåren stöder tillförlitliga kompenserande åtgärder.
Durable Functions använder händelsekällor transparent. I bakgrunden ger operatorn await
(C#) eller yield
(JavaScript/Python) i en orchestrator-funktion kontroll över orchestrator-tråden tillbaka till Durable Task Framework-avsändaren. När det gäller Java finns det inget särskilt språknyckelord. I stället ger anropande .await()
av en uppgift kontroll tillbaka till avsändaren via en anpassad Throwable
. Avsändaren genomför sedan alla nya åtgärder som orkestreringsfunktionen har schemalagt (till exempel att anropa en eller flera underordnade funktioner eller schemalägga en varaktig timer) till lagring. Den transparenta incheckningsåtgärden uppdaterar körningshistoriken för orkestreringsinstansen genom att lägga till alla nya händelser i lagringen, ungefär som en logg med endast tillägg. På samma sätt skapar incheckningsåtgärden meddelanden i lagringen för att schemalägga det faktiska arbetet. Nu kan orkestreringsfunktionen tas bort från minnet. Durable Functions använder som standard Azure Storage som sitt körningstillståndslager, men andra lagringsleverantörer stöds också.
När en orkestreringsfunktion får mer arbete att göra (till exempel ett svarsmeddelande tas emot eller en varaktig timer upphör att gälla) vaknar orkestratorn och kör hela funktionen igen från början för att återskapa det lokala tillståndet. Under reprisen, om koden försöker anropa en funktion (eller utföra något annat asynkront arbete), läser Durable Task Framework körningshistoriken för den aktuella orkestreringen. Om den upptäcker att aktivitetsfunktionen redan har körts och gett ett resultat, spelas funktionens resultat upp igen och orchestrator-koden fortsätter att köras. Reprisen fortsätter tills funktionskoden är klar eller tills den har schemalagt nytt asynkront arbete.
Kommentar
För att uppspelningsmönstret ska fungera korrekt och tillförlitligt måste orchestrator-funktionskoden vara deterministisk. Icke-deterministisk orchestrator-kod kan resultera i körningsfel eller annat oväntat beteende. Mer information om kodbegränsningar för orchestrator-funktioner finns i dokumentationen om begränsningar för orchestrator-funktionskod .
Kommentar
Om en orchestrator-funktion genererar loggmeddelanden kan uppspelningsbeteendet orsaka att dubbletter av loggmeddelanden genereras. Se avsnittet Loggning för att lära dig mer om varför det här beteendet inträffar och hur du kringgår det.
Orkestreringshistorik
Beteendet för händelsekällor i Durable Task Framework är nära kopplat till den orchestrator-funktionskod som du skriver. Anta att du har en aktivitetslänkande orkestreringsfunktion, till exempel följande orchestrator-funktion:
Kommentar
Version 4 av Node.js programmeringsmodellen för Azure Functions är allmänt tillgänglig. Den nya v4-modellen är utformad för att ha en mer flexibel och intuitiv upplevelse för JavaScript- och TypeScript-utvecklare. Läs mer om skillnaderna mellan v3 och v4 i migreringsguiden.
I följande kodfragment anger JavaScript (PM4) programmeringsmodellen V4, den nya upplevelsen.
[FunctionName("HelloCities")]
public static async Task<List<string>> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List<string>();
outputs.Add(await context.CallActivityAsync<string>("SayHello", "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>("SayHello", "Seattle"));
outputs.Add(await context.CallActivityAsync<string>("SayHello", "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
När en aktivitetsfunktion schemaläggs, styr Durable Task Framework körningstillståndet för funktionen till en beständig lagringsserverdel (Azure Table Storage som standard). Det här tillståndet kallas orkestreringshistorik.
Historiktabell
I allmänhet gör Durable Task Framework följande vid varje kontrollpunkt:
- Sparar körningshistorik i beständig lagring.
- Anger meddelanden för funktioner som orkestratorn vill anropa.
- Lägger till meddelanden för själva orkestratorn – till exempel varaktiga timermeddelanden.
När kontrollpunkten är klar kan orkestreringsfunktionen tas bort från minnet tills det finns mer arbete att göra.
Kommentar
Azure Storage ger inga transaktionsgarantier mellan att spara data i tabelllagring och köer. För att hantera fel använder Durable Functions Azure Storage-providern eventuella konsekvensmönster. Dessa mönster säkerställer att inga data går förlorade om det uppstår en krasch eller förlust av anslutning mitt i en kontrollpunkt. Alternativa lagringsproviders, till exempel MSSQL-lagringsprovidern Durable Functions, kan ge starkare konsekvensgarantier.
När den här funktionen har slutförts ser historiken för den tidigare funktionen ut ungefär som i följande tabell i Azure Table Storage (förkortad i illustrationssyfte):
PartitionKey (InstanceId) | EventType | Tidsstämpel | Indata | Namn | Result | Status |
---|---|---|---|---|---|---|
eaee885b | ExecutionStarted | 2021-05-05T18:45:28.852Z | NULL | HelloCities | ||
eaee885b | OrchestratorStarted | 2021-05-05T18:45:32.362Z | ||||
eaee885b | TaskScheduled | 2021-05-05T18:45:32.670Z | SayHello | |||
eaee885b | OrchestratorCompleted | 2021-05-05T18:45:32.670Z | ||||
eaee885b | TaskCompleted | 2021-05-05T18:45:34.201Z | """Hej Tokyo!"" | |||
eaee885b | OrchestratorStarted | 2021-05-05T18:45:34.232Z | ||||
eaee885b | TaskScheduled | 2021-05-05T18:45:34.435Z | SayHello | |||
eaee885b | OrchestratorCompleted | 2021-05-05T18:45:34.435Z | ||||
eaee885b | TaskCompleted | 2021-05-05T18:45:34.763Z | """Hej Seattle!"" | |||
eaee885b | OrchestratorStarted | 2021-05-05T18:45:34.857Z | ||||
eaee885b | TaskScheduled | 2021-05-05T18:45:34.857Z | SayHello | |||
eaee885b | OrchestratorCompleted | 2021-05-05T18:45:34.857Z | ||||
eaee885b | TaskCompleted | 2021-05-05T18:45:34.919Z | """Hej London!"" | |||
eaee885b | OrchestratorStarted | 2021-05-05T18:45:35.032Z | ||||
eaee885b | OrchestratorCompleted | 2021-05-05T18:45:35.044Z | ||||
eaee885b | ExecutionCompleted | 2021-05-05T18:45:35.044Z | "[""Hello Tokyo!"",""Hello Seattle!"",""Hello London!""]" | Slutförd |
Några kommentarer om kolumnvärdena:
- PartitionKey: Innehåller instans-ID:t för orkestreringen.
- EventType: Representerar typen av händelse. Du hittar detaljerade beskrivningar av alla typer av historikhändelser här.
- Tidsstämpel: UTC-tidsstämpeln för historikhändelsen.
- Namn: Namnet på den funktion som anropades.
- Indata: JSON-formaterade indata för funktionen.
- Resultat: Funktionens utdata, dvs. dess returvärde.
Varning
Även om det är användbart som ett felsökningsverktyg bör du inte vara beroende av den här tabellen. Det kan ändras i takt med att Durable Functions-tillägget utvecklas.
Varje gång funktionen återupptas efter att ha väntat på att en uppgift ska slutföras kör Durable Task Framework orchestrator-funktionen från grunden igen. Vid varje omkörning läser den körningshistoriken för att avgöra om den aktuella asynkrona aktiviteten har slutförts. Om körningshistoriken visar att aktiviteten redan har slutförts spelar ramverket upp utdata för aktiviteten och går vidare till nästa uppgift. Den här processen fortsätter tills hela körningshistoriken har spelats upp igen. När den aktuella körningshistoriken har spelats upp igen har de lokala variablerna återställts till sina tidigare värden.
Funktioner och mönster
I nästa avsnitt beskrivs funktionerna och mönstren för orkestreringsfunktioner.
Sub-orkestreringar
Orchestrator-funktioner kan anropa aktivitetsfunktioner, men även andra orkestreringsfunktioner. Du kan till exempel skapa en större orkestrering av ett bibliotek med orkestreringsfunktioner. Eller så kan du köra flera instanser av en orchestrator-funktion parallellt.
Mer information och exempel finns i artikeln Underorkestreringar .
Varaktiga timers
Orkestreringar kan schemalägga varaktiga timers för att implementera fördröjningar eller för att konfigurera timeouthantering för asynkrona åtgärder. Använd varaktiga timers i orchestrator-funktioner i stället för språkbaserade "sömn"-API:er.
Mer information och exempel finns i artikeln Durable timers (Varaktiga timers ).
Externa händelser
Orchestrator-funktioner kan vänta tills externa händelser uppdaterar en orkestreringsinstans. Den här durable functions-funktionen är ofta användbar för att hantera en mänsklig interaktion eller andra externa återanrop.
Mer information och exempel finns i artikeln Externa händelser .
Felhantering
Orchestrator-funktioner kan använda felhanteringsfunktionerna i programmeringsspråket. Befintliga mönster som try
/catch
stöds i orkestreringskod.
Orkestreringsfunktioner kan också lägga till återförsöksprinciper till de aktivitets- eller underorkestreringsfunktioner som de anropar. Om en aktivitet eller underorkestreringsfunktion misslyckas med ett undantag kan den angivna återförsöksprincipen automatiskt fördröja och försöka köra körningen igen upp till ett angivet antal gånger.
Kommentar
Om det finns ett ohanterat undantag i en orkestreringsfunktion slutförs orkestreringsinstansen i ett Failed
tillstånd. Det går inte att försöka utföra en orkestreringsinstans igen när den har misslyckats.
Mer information och exempel finns i artikeln Felhantering .
Kritiska avsnitt (Durable Functions 2.x, för närvarande endast .NET)
Orkestreringsinstanser är enkeltrådade så det är inte nödvändigt att oroa sig för rasförhållanden inom en orkestrering. Tävlingsförhållanden är dock möjliga när orkestreringar interagerar med externa system. För att minimera konkurrensförhållanden när du interagerar med externa system kan orkestreringsfunktioner definiera kritiska avsnitt med hjälp av en LockAsync
metod i .NET.
Följande exempelkod visar en orchestrator-funktion som definierar ett kritiskt avsnitt. Den anger det kritiska avsnittet med hjälp av LockAsync
-metoden. Den här metoden kräver att du skickar en eller flera referenser till en varaktig entitet som durably hanterar låstillståndet. Endast en enda instans av orkestreringen kan köra koden i det kritiska avsnittet i taget.
[FunctionName("Synchronize")]
public static async Task Synchronize(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var lockId = new EntityId("LockEntity", "MyLockIdentifier");
using (await context.LockAsync(lockId))
{
// critical section - only one orchestration can enter at a time
}
}
LockAsync
Hämtar de varaktiga låsen och returnerar ett IDisposable
som avslutar det kritiska avsnittet när det tas bort. Det här IDisposable
resultatet kan användas tillsammans med ett using
block för att få en syntaktisk representation av det kritiska avsnittet. När en orkestreringsfunktion anger ett kritiskt avsnitt kan endast en instans köra kodblocket. Andra instanser som försöker komma in i det kritiska avsnittet blockeras tills den föregående instansen avslutar det kritiska avsnittet.
Den viktiga avsnittsfunktionen är också användbar för att samordna ändringar i varaktiga entiteter. Mer information om kritiska avsnitt finns i avsnittet Durable entiteter "Entitetssamordning" .
Kommentar
Viktiga avsnitt finns i Durable Functions 2.0. För närvarande är det bara .NET-orkestreringar i processen som implementerar den här funktionen. Entiteter och kritiska avsnitt är ännu inte tillgängliga i Durable Functions för dotnet-isolerad arbetare.
Anropa HTTP-slutpunkter (Durable Functions 2.x)
Orchestrator-funktioner har inte behörighet att göra I/O, enligt beskrivningen i begränsningar för orchestrator-funktionskod. Den vanliga lösningen för den här begränsningen är att omsluta all kod som behöver göra I/O i en aktivitetsfunktion. Orkestreringar som interagerar med externa system använder ofta aktivitetsfunktioner för att göra HTTP-anrop och returnera resultatet till orkestreringen.
För att förenkla det här vanliga mönstret kan orchestrator-funktioner använda CallHttpAsync
metoden för att anropa HTTP-API:er direkt.
[FunctionName("CheckSiteAvailable")]
public static async Task CheckSiteAvailable(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
Uri url = context.GetInput<Uri>();
// Makes an HTTP GET request to the specified endpoint
DurableHttpResponse response =
await context.CallHttpAsync(HttpMethod.Get, url);
if ((int)response.StatusCode == 400)
{
// handling of error codes goes here
}
}
Förutom att stödja grundläggande mönster för begäran/svar stöder metoden automatisk hantering av vanliga asynkrona HTTP 202-avsökningsmönster och stöder även autentisering med externa tjänster med hanterade identiteter.
Mer information och detaljerade exempel finns i artikeln HTTP-funktioner .
Kommentar
Att anropa HTTP-slutpunkter direkt från orchestrator-funktioner är tillgängligt i Durable Functions 2.0 och senare.
Skicka flera parametrar
Det går inte att skicka flera parametrar direkt till en aktivitetsfunktion. Rekommendationen är att skicka in en matris med objekt eller sammansatta objekt.
I .NET kan du också använda ValueTuple-objekt . Följande exempel använder nya funktioner i ValueTuple som lagts till med C# 7:
[FunctionName("GetCourseRecommendations")]
public static async Task<object> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
string major = "ComputerScience";
int universityYear = context.GetInput<int>();
object courseRecommendations = await context.CallActivityAsync<object>(
"CourseRecommendations",
(major, universityYear));
return courseRecommendations;
}
[FunctionName("CourseRecommendations")]
public static async Task<object> Mapper([ActivityTrigger] IDurableActivityContext inputs)
{
// parse input for student's major and year in university
(string Major, int UniversityYear) studentInfo = inputs.GetInput<(string, int)>();
// retrieve and return course recommendations by major and university year
return new
{
major = studentInfo.Major,
universityYear = studentInfo.UniversityYear,
recommendedCourses = new []
{
"Introduction to .NET Programming",
"Introduction to Linux",
"Becoming an Entrepreneur"
}
};
}