Home / Nieuws / Azure Durable Functions – Deel 1
25 november 2022
Door: Sunita Kawale

Azure Durable Functions – Deel 1

Wat is Azure-functions?

Azure Functions is het Function-as-a-Service-aanbod van Azure.

Een serverloze cloudomgeving waarin jouw applicatie kan worden uitgevoerd met minder code, minder infrastructuuronderhoud en kostenbesparingen. Dit helpt de implementatietijd en zorgen over onderhoud te verminderen. Elke Azure-functie heeft een trigger, waardoor deze wordt uitgevoerd. Bijvoorbeeld het uitvoeren van jouw code nadat een record uit de database is verwijderd of een nieuwe gebruiker is gemaakt of gebruikersmachtigingen zijn gewijzigd. Bovendien zijn de eerste uitvoeringen van jouw functie/applicaties gratis.

 

Wat is een durable function?

Het uitvoeren van stateful functies in een serverloze cloudomgeving is een durable function. Het is een uitbreiding van de Azure-functie waarmee je stateful workflows kunt schrijven. Durable functions zijn verdeeld in twee delen: één zijn Orchestrator-functies die ons helpen bij het schrijven van de stateful workflows en de tweede zijn Entity-functies om de stateful-objecten van de workflow/programma’s te verwerken. In durable functions kunnen we de herstart van de functie, de status van de functie beheren en ook de controlepunten toevoegen, zodat we ons kunnen concentreren op de bedrijfsontwikkeling.

 

Ondersteunende talen van durable functions?

Vrijwel alle talen worden ondersteund door een durable functions die werkt met de normale Azure-functies, maar er is een minimumvereiste voor alle talen, zoals weergegeven in de onderstaande tabel.

Language Stack Azure Functions Runtime versions Language worker role version Minimum bundle version
.NET / C# / F# Functions 1.0+ In-process (GA) Out-of-process N/a.
Javascript/Typescript Functions 2.0+ Node 8+ 2.x bundles
Python Functions 2.0+ Python 3.7+ 2.x bundles
PowerShell Functions 3.0+ PowerShell 7+ 2.x bundles
Java (preview) Functions 3.0+ Java 8+ 4.x bundles

Welke Soorten durable functions zijn er?

  1. Function Chaining (Behandeld in deze blog)
  2. Fan-Out/Fan-In (Behandeld in deze blog)
  3. Async HTTP APIs
  4. Monitoring
  5. Human interaction
  6. Aggregator (Stateful entities)

 

Wat is function chaining?

Na elke functie wordt een reeks functies uitgevoerd en de volgende functie is afhankelijk van de output van de vorige functie, genaamd function linking or chaining

In het onderstaande diagram kunnen we zien dat functie F2 afhangt van de uitvoer van functie F1 enzovoort.

Een durable function bewaakt de status van de uitvoering van de functie. Terwijl de uitvoering van het functiesysteem is mislukt, start de durable function de functie-instantie vanaf het vorige punt.

Voorbeeld van function Chaining

Bedenk dat we een workflow moeten maken voor de gebruikersregistratie, bij succesvolle registratie zullen we de machtigingen toewijzen en vervolgens een bevestigingsmail sturen.

In het onderstaande voorbeeld hebben we een eenvoudige workflow-functie ChainingFunction gemaakt met drie activiteiten. De eerste activiteits-functie is RegisterUser. Zodra we het resultaat van de eerste activiteits-functie hebben ontvangen, sturen we het resultaat naar de volgende activiteits-functie AssignUserPermissions en vervolgens de derde activiteits-functie SendConfirmationEmail email.

[FunctionName("ChainingFunction")]
public static async Task<string> RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log)
{
    try
    {
       var user = await context.CallActivityAsync<string>("RegisterUser", "Sunita");
       var assignedPermissions =  context.CallActivityAsync<bool>("AssignUserPermissions", user);
       var mailSend =  context.CallActivityAsync<bool>("SendConfirmationEmail", user);
       return user;
    }
    catch (Exception ex)
    {
        log.LogError($"Unhandled exception Message {ex.Message}. {ex.StackTrace}");
    }

    return string.Empty;
}

[FunctionName("RegisterUser")]
public static async Task<string> CreateUser([ActivityTrigger] string name, ILogger log)
{
    var createUser = await Task.Factory.StartNew<string>(() => $"{name}@test.com");
    log.LogInformation($"Saying hello to {name}.");
    return createUser;
}

[FunctionName("AssignUserPermissions")]
public static async Task<bool> AssignUserPermissions([ActivityTrigger] string userName, ILogger log)
{
    var assignPermissions = await Task.Factory.StartNew<bool>(() =>
    {
        Thread.Sleep(30);
        return true;
    });
    return assignPermissions;
}

[FunctionName("SendConfirmationEmail")]
public static async Task<bool> SendConfirmationEmail([ActivityTrigger] string userName, ILogger log)
{
    var sendEmail = await Task.Factory.StartNew<bool>(() =>
    {
        Thread.Sleep(30);
        return true;
    });
    return sendEmail;
}

Wat is Fan-Out/Fan-in?

Het parallel verwerken van de meerdere functies en het vervolgens aggregeren van de resultaten wordt Fan-out/Fan-in genoemd.

In het onderstaande diagram start functie F1 meerdere functies F2 om parallel uit te voeren en functie F3 zal de aggregaties doen op de resultaten van F2.

Voorbeeld van Fan-Out/Fan-In

Stel je voor dat we op een universiteit de drie beste studenten moeten vinden op basis van het totale aantal punten dat ze voor elk vak hebben behaald.

Om bovenstaande doelstelling te realiseren, creëren we een Fan-Out/Fan-In durable function.

GetTotalMarks is de activiteits-functie die we parallel uitvoeren voor het totale aantal punten van elke student. We wachten tot alle studenten het totale aantal punten hebben behaald en zoeken dan de beste N studenten door de activiteits-functie GetTopNStudent aan te roepen.

[FunctionName("FanOutFanInFunction")]
public static async Task<List<Student>> RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var parallelTasks = new List<Task<Student>>();
    RequestTopNStudents inputdata = con-text.GetInput<RequestTopNStudents>();

    // Get a list of N work items to process in parallel.
    try
    {
        if (inputdata != null)
        {
            foreach (var item in inputdata.Students)
            {
                Task<Student> task = con-text.CallActivityAsync<Student>("GetTotalMarks", item);
                parallelTasks.Add(task);
            }
            var all = await Task.WhenAll(parallelTasks);
            int topN  = inputdata.TopNStudents;
            (List<Student>, int) inputs = new(all.ToList(), topN); //value tuple to send multiple parameters to activity function
            
            List<Student> topNStudents = await con-text.CallActivityAsync<List<Student>>("GetTopNStudent", inputs);
            return topNStudents;
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("error {0}", ex);
    }
    return null;
}

[FunctionName("GetTotalMarks")]
public static Student GetTotalMarks([ActivityTrigger] Student student)
{
    student.Total = student.MathsMarks + student.PhysicsMarks + stu-dent.ChemistryMarks;
    return student;
} 

[FunctionName("GetTopNStudent")]
public static List<Student> GetTopNStudent([ActivityTrigger] IDurableActiv-ityContext inputs)
{
    var (students, topN) = inputs.GetInput<(List<Student>, int)>();

    students.Sort((a, b) => b.Total.CompareTo(a.Total));
    return students.Take(topN).ToList();
} 
 

Referentie document: Azure Functions | overview Microsoft Learn

In het volgende deel behandelen we de verschillende soorten durable functions en manieren om te implementeren op Azure.