Wat is Durable Functions?
Durable Functions is een extensie van Azure Functions waarmee u stateful functies kunt schrijven in een serverloze rekenomgeving. Met de extensie kunt u stateful werkstromen definiëren door Orchestrator-functies te schrijven en stateful entiteiten door entiteitsfuncties te schrijven met behulp van het Azure Functions-programmeermodel. Achter de schermen beheert de extensie status, controlepunten en het opnieuw opstarten, zodat u zich kunt concentreren op uw bedrijf.
Ondersteunde talen
Durable Functions ondersteunt momenteel de volgende talen:
- C#: zowel vooraf gecompileerde klassebibliotheken als C#-script.
- JavaScript: alleen ondersteund voor versie 2.x of hoger van de Azure Functions runtime. Versie 1.7.0 of hoger van de Durable Functions-extensie vereist.
- Python: vereist versie 2.3.1 of hoger van de Durable Functions-extensie.
- F#: vooraf gecompileerde klassebibliotheken en F#-script. F#-script wordt alleen ondersteund voor versie 1.x van de Azure Functions-runtime.
- PowerShell: alleen ondersteund voor versie 3.x van de Azure Functions runtime en PowerShell 7. Vereist versie 2.x van de bundelextensies.
Voor toegang tot de nieuwste functies en updates wordt aanbevolen om de nieuwste versies van de Durable Functions-extensie en de taalspecifieke Durable Functions-bibliotheken te gebruiken. Meer informatie over versies van Durable Functions.
Durable Functions heeft als doel alle Azure Functions-talen te ondersteunen. Zie Durable Functions issues list voor de laatste voortgangsstatus van de ondersteuning voor meer talen.
Net als bij Azure Functions zijn er sjablonen om u te helpen bij het ontwikkelen van Durable Functions met behulp van Visual Studio 2019, Visual Studio Code en de Azure-portal.
Toepassingspatronen
De primaire use case voor Durable Functions is het vereenvoudigen van complexe stateful coördinatievereisten in serverloze toepassingen. In de volgende secties worden enkele typische toepassingspatronen beschreven die kunnen profiteren van Durable Functions:
- Functiekoppeling
- Fan-out/fan-in
- Asynchrone HTTP-API's
- Controle
- Menselijke tussenkomst
- Aggregator (stateful-entiteiten)
Patroon #1: Functiekoppeling
In het patroon voor functiekoppeling wordt een reeks functies in een specifieke volg orde uitgevoerd. In dit patroon wordt de uitvoer van een functie toegepast op de invoer van een andere functie.

U kun Durable Functions gebruiken om het patroon voor functiekoppeling bondig te implementeren zoals in het volgende voorbeeld.
In dit voorbeeld zijn de waarden F1, F2, F3 en F4 de namen van andere functies in dezelfde functie-app. U kunt een controlestroom implementeren met behulp van normale imperatieve codeconstructies. Code wordt van boven naar beneden uitgevoerd. De code kan bestaande taalsemantiek voor controlestromen bevatten, zoals voorwaarden en lussen. U kunt logica voor foutafhandeling toevoegen in try/catch/finally blokken.
[FunctionName("Chaining")]
public static async Task<object> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
try
{
var x = await context.CallActivityAsync<object>("F1", null);
var y = await context.CallActivityAsync<object>("F2", x);
var z = await context.CallActivityAsync<object>("F3", y);
return await context.CallActivityAsync<object>("F4", z);
}
catch (Exception)
{
// Error handling or compensation goes here.
}
}
U kunt de context-parameter gebruiken om andere functies aan te roepen op naam, parameters door te geven en functie-uitvoer te retourneren. Telkens de code await aanroept controleert het Durable Functions-framework de voortgang van de huidige functie-instantie. Als het proces of de virtuele machine halverwege tijdens de uitvoering recycleert, dan begint de functie-instantie opnieuw vanaf de voorgaande await-aanroep. Zie de volgende sectie, Patroon #2, voor meer informatie: Uitwaaieren/inwaaieren.
Patroon #2: Uitwaaieren/inwaaieren
In het patroon uitwaaieren/inwaaieren voert u meerdere functies parallel uit en wacht u totdat alle functies zijn voltooid. Vaak wordt er een aggregatie toegepast op de resultaten die worden geretourneerd door de functies.

Bij normale functies kunt u uitwaaieren door de functie meerdere berichten te laten sturen naar een wachtrij. Terug inwaaieren is veel lastiger. Om in een normale functie in te waaieren schrijft u code om op te volgen wanneer de in de wachtrij geactiveerde functies aflopen en slaat u vervolgens de functie-uitvoer op.
De Durable Functions-extensie verwerkt dit patroon met relatief eenvoudige code:
[FunctionName("FanOutFanIn")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var parallelTasks = new List<Task<int>>();
// Get a list of N work items to process in parallel.
object[] workBatch = await context.CallActivityAsync<object[]>("F1", null);
for (int i = 0; i < workBatch.Length; i++)
{
Task<int> task = context.CallActivityAsync<int>("F2", workBatch[i]);
parallelTasks.Add(task);
}
await Task.WhenAll(parallelTasks);
// Aggregate all N outputs and send the result to F3.
int sum = parallelTasks.Sum(t => t.Result);
await context.CallActivityAsync("F3", sum);
}
Het uitwaaierwerk wordt verdeeld over meerdere instanties van de F2-functie. Het werk wordt opgevolgd met een dynamische takenlijst. Task.WhenAll wordt aangeroepen om te wachten tot alle aangeroepen functies voltooid zijn. Vervolgens wordt de F2-functie-uitvoer samengevoegd uit de dynamische takenlijst en doorgegeven naar de F3-functie.
Het automatisch plaatsen van controlepunten bij de await-aanroep op Task.WhenAll zorgt ervoor dat een reeds voltooide taak niet opnieuw moet worden opgestart bij een potentiële crash of reboot halverwege.
Notitie
In zeldzame gevallen is het mogelijk dat er een crash optreedt in het venster nadat een activiteitsfunctie wordt voltooid maar voor de voltooiing wordt opgeslagen in de indelingsgeschiedenis. Als dit gebeurt, dan wordt de activiteitsfunctie opnieuw uitgevoerd vanaf het begin nadat het proces hersteld wordt.
Patroon #3: Asynchrone HTTP-API's
Het asynchrone HTTP API-patroon biedt een oplossing voor het probleem van de coördinatie van langdurige bewerkingen met externe clients. Een gebruikelijke manier om dit patroon te implementeren is door de langdurige actie te laten activeren door een HTTP-eindpunt. Leid de client dan om naar een statuseindpunt dat de client bevraagt om te weten te komen wanneer de bewerking is voltooid.

Durable Functions biedt ingebouwde ondersteuning voor dit patroon, waardoor u minder complexe of zelfs helemaal geen code moet schrijven om te communiceren met de uitvoering van langdurige functies. De voorbeelden van de quickstart Durable Functions (C# en JavaScript) laten een eenvoudige REST-opdracht zien die u kunt gebruiken om een nieuwe instantie van een orchestrator-functie te starten. Nadat een instantie is gestart, worden de HTTP-API's van de webhook met de status van de orchestrator-functie weergegeven.
Het volgende voorbeeld geeft REST-opdrachten weer die een orchestrator starten en de status opvragen. Voor de duidelijkheid zijn bepaalde protocoldetails weggelaten uit het voorbeeld.
> curl -X POST https://myfunc.azurewebsites.net/api/orchestrators/DoWork -H "Content-Length: 0" -i
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: https://myfunc.azurewebsites.net/runtime/webhooks/durabletask/instances/b79baf67f717453ca9e86c5da21e03ec
{"id":"b79baf67f717453ca9e86c5da21e03ec", ...}
> curl https://myfunc.azurewebsites.net/runtime/webhooks/durabletask/instances/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: https://myfunc.azurewebsites.net/runtime/webhooks/durabletask/instances/b79baf67f717453ca9e86c5da21e03ec
{"runtimeStatus":"Running","lastUpdatedTime":"2019-03-16T21:20:47Z", ...}
> curl https://myfunc.azurewebsites.net/runtime/webhooks/durabletask/instances/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 200 OK
Content-Length: 175
Content-Type: application/json
{"runtimeStatus":"Completed","lastUpdatedTime":"2019-03-16T21:20:57Z", ...}
Aangezien de Durable Functions-runtime de status voor u beheert, moet u geen eigen mechanisme voor het opvolgen van de status meer implementeren.
De Durable Functions-extensie laat ingebouwde HTTP API's zien die langlopende indelingen beheren. U kunt dit patroon ook zelf implementeren met behulp van uw eigen functie-triggers (zoals HTTP, een wachtrij of Azure Event Hubs) en de indelingsclientbinding. Zo kunt u bijvoorbeeld een wachtrijbericht gebruiken om beëindiging te activeren. Of u kunt een HTTP-trigger gebruiken die beschermd wordt door een Azure Active Directory-verificatiebeleid in plaats van de ingebouwde HTTP API's die een gegenereerde sleutel gebruiken voor verificatie.
Lees het artikel HTTP-functies voor meer informatie over hoe u asynchrone, langdurende processen via HTTP kunt weergeven met behulp van de Durable Functions-extensie.
Patroon #4: Controleren
Het monitorpatroon verwijst naar een flexibel, terugkerend proces in een werkstroom. Een voorbeeld daarvan is navragen tot er aan specifieke voorwaarden voldaan is. U kunt een gewone timertrigger gebruiken voor een basisscenario, zoals een periodieke opschoningstaak, maar het interval is statisch en het beheer van de levensduur wordt complex. U kunt Durable Functions gebruiken om flexibele, terugkerende intervallen te creëren, de levensduur van taken te beheren en meerdere bewakingsprocessen maken vanuit één indeling.
Een voorbeeld van het bewakingspatroon is om het eerdere asynchrone HTTP API-scenario om te keren. In plaats van een eindpunt weer te geven waarmee een externe client een langdurige bewerking bewaakt, gebruikt de langdurige bewaking een extern eindpunt en wacht het tot de status verandert.

Met enkele regels code kunt u Durable Functions gebruiken om meerdere bewakingen te maken die willekeurige eindpunten observeren. De bewakingen kunnen de uitvoering stopzetten wanneer er aan een voorwaarde is voldaan, of een andere functie kan de duurzame indelingsclient gebruiken om de bewakingen te beëindigen. U kunt het wait-interval van een bewaking veranderen op basis van een specifieke voorwaarde (bijvoorbeeld exponentieel uitstel).
Met de volgende code wordt een standaardbewaking geïmplementeerd:
[FunctionName("MonitorJobStatus")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
int jobId = context.GetInput<int>();
int pollingInterval = GetPollingInterval();
DateTime expiryTime = GetExpiryTime();
while (context.CurrentUtcDateTime < expiryTime)
{
var jobStatus = await context.CallActivityAsync<string>("GetJobStatus", jobId);
if (jobStatus == "Completed")
{
// Perform an action when a condition is met.
await context.CallActivityAsync("SendAlert", machineId);
break;
}
// Orchestration sleeps until this time.
var nextCheck = context.CurrentUtcDateTime.AddSeconds(pollingInterval);
await context.CreateTimer(nextCheck, CancellationToken.None);
}
// Perform more work here, or let the orchestration end.
}
Wanneer een verzoek ontvangen is, wordt er een nieuwe indelingsinstantie gemaakt voor die taak-id. De instantie controleert een status tot er aan een voorwaarde is voldaan en de lus wordt afgesloten. Een duurzame timer controleert het polling-interval. Vervolgens kan er meer werk worden uitgevoerd of kan de indeling beëindigd worden. Wanneer nextCheck expiryTime overschrijdt eindigt de bewaking.
Patroon #5: Menselijke tussenkomst
Veel geautomatiseerde processen bevatten een menselijke tussenkomst. Mensen betrekken bij een geautomatiseerd proces is lastig, want mensen zijn niet zo beschikbaar en responsief als cloudservices. Een geautomatiseerd proces kan deze tussenkomst mogelijk maken door time-outs en compensatielogica te gebruiken.
Een goedkeuringsproces is een voorbeeld van een bedrijfsproces waar menselijke tussenkomst aan te pas komt. Zo kan de goedkeuring van een manager vereist zijn voor een onkostennota die een bepaald bedrag overschrijdt. Als de manager de onkostennota niet binnen de 72 uur goedkeurt (bijvoorbeeld omdat hij met vakantie is), dan wordt de escalatieprocedure opgestart om goedkeuring te krijgen van iemand anders (bijvoorbeeld de manager van de manager).

U kunt het patroon in dit voorbeeld implementeren met behulp van een orchestrator-functie. De orchestrator gebruikt een duurzame timer om goedkeuring te vragen. De orchestrator escaleert wanneer er een time-out optreedt. De orchestrator wacht op een externe gebeurtenis zoals een melding die gegenereerd wordt door een menselijke tussenkomst.
In deze voorbeelden wordt een goedkeuringsproces gemaakt om het menselijke tussenkomstpatroon te demonstreren:
[FunctionName("ApprovalWorkflow")]
public static async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
await context.CallActivityAsync("RequestApproval", null);
using (var timeoutCts = new CancellationTokenSource())
{
DateTime dueTime = context.CurrentUtcDateTime.AddHours(72);
Task durableTimeout = context.CreateTimer(dueTime, timeoutCts.Token);
Task<bool> approvalEvent = context.WaitForExternalEvent<bool>("ApprovalEvent");
if (approvalEvent == await Task.WhenAny(approvalEvent, durableTimeout))
{
timeoutCts.Cancel();
await context.CallActivityAsync("ProcessApproval", approvalEvent.Result);
}
else
{
await context.CallActivityAsync("Escalate", null);
}
}
}
Roep context.CreateTimer aan om de duurzame timer te maken. De melding wordt ontvangen door context.WaitForExternalEvent. Vervolgens wordt Task.WhenAny aangeroepen om te bepalen of er geëscaleerd wordt (time-out vindt eerst plaats) of dat de goedkeuring verwerkt wordt (de goedkeuring wordt ontvangen voor de time-out).
Een externe client kan de gebeurtenismelding afleveren aan een wachtende orchestrator-functie met behulp van de ingebouwde HTTP API's:
curl -d "true" http://localhost:7071/runtime/webhooks/durabletask/instances/{instanceId}/raiseEvent/ApprovalEvent -H "Content-Type: application/json"
Een gebeurtenis kan ook worden gegenereerd met behulp van de duurzame indelingsclient van een andere functie in dezelfde functie-app:
[FunctionName("RaiseEventToOrchestration")]
public static async Task Run(
[HttpTrigger] string instanceId,
[DurableClient] IDurableOrchestrationClient client)
{
bool isApproved = true;
await client.RaiseEventAsync(instanceId, "ApprovalEvent", isApproved);
}
Patroon #6: Aggregator (stateful entiteiten)
Het zesde patroon is het samenvoegen van gebeurtenisgegevens gedurende een bepaalde periode in één enkele, adresseerbare entiteit. In dit patroon kunnen de gegevens die samengevoegd worden afkomstig zijn van verschillende bronnen, geleverd worden in batches of verspreid worden over een lange periode. De aggregator moet mogelijk actie ondernemen op de gebeurtenisgegevens wanneer die aankomen, en de externe clients moeten mogelijk de samengevoegde gegevens onderzoeken.

Wat de implementatie van dit patroon met normale, staatloze functies lastig maakt is dat het beheer van gelijktijdigheid erg moeilijk wordt. Niet alleen bewerken verschillende threads tegelijkertijd dezelfde gegevens, maar u dient er ook op te letten dat de aggregator slechts op één virtuele machine tegelijkertijd wordt uitgevoerd.
U kunt Duurzame entiteiten gebruiken om dit patroon eenvoudig als een enkele functie te implementeren.
[FunctionName("Counter")]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
int currentValue = ctx.GetState<int>();
switch (ctx.OperationName.ToLowerInvariant())
{
case "add":
int amount = ctx.GetInput<int>();
ctx.SetState(currentValue + amount);
break;
case "reset":
ctx.SetState(0);
break;
case "get":
ctx.Return(currentValue);
break;
}
}
Duurzame entiteiten kunnen ook gemodelleerd worden als klassen in .NET. Dit model kan handig zijn als de lijst met bewerkingen vaststaat en groot wordt. Het volgende voorbeeld is een equivalente implementatie van de Counter-entiteit met .NET-klassen en -methodes.
public class Counter
{
[JsonProperty("value")]
public int CurrentValue { get; set; }
public void Add(int amount) => this.CurrentValue += amount;
public void Reset() => this.CurrentValue = 0;
public int Get() => this.CurrentValue;
[FunctionName(nameof(Counter))]
public static Task Run([EntityTrigger] IDurableEntityContext ctx)
=> ctx.DispatchAsync<Counter>();
}
Clients kunnen bewerkingen in de wachtrij plaatsen (ook bekend als 'signalering') voor een entiteitsfunctie met behulp van de entiteitsclientbinding.
[FunctionName("EventHubTriggerCSharp")]
public static async Task Run(
[EventHubTrigger("device-sensor-events")] EventData eventData,
[DurableClient] IDurableEntityClient entityClient)
{
var metricType = (string)eventData.Properties["metric"];
var delta = BitConverter.ToInt32(eventData.Body, eventData.Body.Offset);
// The "Counter/{metricType}" entity is created on-demand.
var entityId = new EntityId("Counter", metricType);
await entityClient.SignalEntityAsync(entityId, "add", delta);
}
Notitie
Dynamisch gegenereerde proxy's zijn ook beschikbaar in .NET om entiteiten op typebeveiligde wijze te signaleren. Naast signaleren kunnen clients de status van een entiteitsfunctie ook opvragen met typebeveiligde methodes op de indelingsclientbinding.
Entiteitsfuncties zijn beschikbaar in Durable Functions 2.0 en hoger voor C#, JavaScript en Python.
De technologie
Achter de schermen is de Durable Functions-extensie gebouwd op het Durable Task Framework, een opensource-bibliotheek in GitHub die gebruikt wordt om werkstromen te bouwen in code. Net zoals Azure Functions de serverloze evolutie van Azure WebJobs is, is Durable Functions de serverloze evolutie van het Durable Task Framework. Microsoft en andere organisaties gebruiken het Durable Task Framework om bedrijfskritieke processen te automatiseren. Het sluit naadloos aan op de serverloze Azure Functions-omgeving.
Codebeperkingen
Om betrouwbare en langdurige uitvoeringsgaranties te bieden, beschikken orchestrator-functies over een set coderegels die gevolg moeten worden. Zie het artikel Codebeperkingen voor de orchestrator-functie.
Billing
Durable Functions worden op dezelfde manier in rekening gebracht als Azure Functions. Zie Prijzen voor Azure Functions voor meer informatie. Wanneer u orchestrator-functies in het Verbruiksabonnement van Azure Functions uitvoert, dient u op enkele factureringseigenschappen te letten. Zie het artikel Durable Functions-facturering voor meer informatie hierover.
Duik er meteen in
U kunt in minder dan 10 minuten aan de slag gaan met Durable Functions door een van deze taalspecifieke quickstart-zelfstudies te volgen:
- C# met Visual Studio 2019
- JavaScript met Visual Studio Code
- Python met Visual Studio Code
- PowerShell met Visual Studio Code
In deze quickstarts maakt en test u een lokale duurzame 'hallo wereld'-functie. Vervolgens publiceert u de functiecode op Azure. De functie die u maakt, organiseert en koppelt aanroepen naar andere functies.
Publicaties
Durable Functions is ontwikkeld in samenwerking met Microsoft Research. Als gevolg hiervan produceert het Durable Functions actief onderzoeksdocumenten en artefacten; Dit zijn onder andere:
- Durable Functions: Semantics for Stateful Serverless (OOPSLA'21)
- Serverloze werkstromen met Durable Functions en Netherite (vooraf afdrukken)
Meer informatie
De volgende video laat de voordelen van Durable Functions zien:
Voor een uitgebreidere bespreking van Durable Functions en de onderliggende technologie bekijkt u de volgende video (deze focust op .NET, maar de concepten zijn ook van toepassing op andere ondersteunde talen):
Omdat Durable Functions een geavanceerde extensie voor Azure Functions is, is het niet geschikt voor alle toepassingen. Zie voor een vergelijking met andere Azure-technologieën Compare Azure Functions and Azure Logic Apps.