Comment créer des workflows complexes avec les Azure Fonctions ?

Quand on pense aux Azure Fonctions, on pense à un bout de code simple qui s’exécute dans un environnement Serverless. C’est vrai, mais on peut également implémenter des choses beaucoup plus complexes avec l’extension Fonction Durable.
Cet article traitera trois fonctionnalités des Fonctions Durables :
- Fonction cliente : c’est le point d’entrée de notre orchestration ;
- Fonction d’orchestration, qui orchestre nos actions (fonction d’activité, sub-orchestrateur, attente d’événements externes, Timer…) ;
- Fonction d’activité(s) (similaire à une Azure Fonction), qui contient le code d’une action (exécute une opération, effectue des appels réseau, retourne des données à l’orchestrateur appelant, etc.).
Pour plus d’informations sur les Fonctions Durables, je vous invite à consulter la documentation Microsoft.
Fonctions vs Logic apps
Il existe dans Azure deux principales façons d’implémenter des workflows : les Logic Apps et les Azure Fonctions avec l’extension Fonction Durable. N’hésitez pas à consulter cet article de Microsoft comparant les 2 solutions.
Nous traiterons aujourd’hui les Azure Fonctions.
Pourquoi implémenter ses workflows dans un environnement Serveless plutôt que dans son application web ?
Mettons ici l’accent sur les Azure Fonctions, et notamment sur les Fonctions Durables :
- Abstraction des serveurs: grâce au Serverless computing, le développeur met le focus sur le développement du code et l’orchestration des fonctions. La gestion de l’infrastructure est assurée par Azure et votre application monte ou descend automatiquement en puissance en fonction de la charge.
- Multiples déclencheurs: HTTP, Timer, file d’attente, Blob Storage, etc.
- Création d’orchestrations complexes de longue durée avec état (c’est le sujet de cet article 🙂)
- Microfacturation: contrairement aux App Services, le Serverless facture au temps d’exécution. Si une Azure Fonction est déclenchée une fois dans la journée pour un temps d’exécution de 2 minutes, vous ne serez facturé que pour 2 minutes. A l’inverse, si une API hébergée sur un App Service n’enregistre qu’un seul accès dans la journée, vous serez facturé pour la journée entière de disponibilité.
- Maintenance : pour mettre à jour un workflow, il suffit juste de redéployer l’Azure Fonction concernée et non pas toute l’application web.
Exemple de mise en application
Rien ne vaut un exemple concret pour comprendre toutes ces notions !
Avant de présenter le scénario, quelques points importants doivent être soulignés :
- Il est recommandé qu’une fonction d’activité n’ait qu’une seule responsabilité. Les Fonctions Durables ont un excellent système de retry: de ce fait, si une stratégie de retry est configurée, en cas d’échec d’une des Fonctions d’Activité, une nouvelle tentative sera lancée automatiquement et les autres Fonctions d’Activité en succès ne seront pas relancées.
- Le code dans les Orchestrateurs ne peut appeler que des APIs déterministes, c’est-à-dire des APIs qui retournent toujours la même valeur pour la même entrée. La principale raison est qu’une Fonction d’Orchestration va être rejouée plusieurs fois et enregistre l’historique d’exécution dans une table (du storage account lié à l’Azure Fonction – pour plus d’informations sur le fonctionnement des Fonctions Durables: https://docs.microsoft.com/fr-fr/azure/azure-functions/durable/durable-functions-orchestrations?tabs=csharp#reliability).
- Exemple d’API à éviter : API retournant la date courante (DateTime.Now) : pour contourner ce problème, deux solutions sont possibles : utiliser une fonction d’activité qui renvoie la date courante – utiliser la propriété CurrentUtcDateTime du IDurableOrchestrationContext (liste complète : https://docs.microsoft.com/fr-fr/azure/azure-functions/durable/durable-functions-code-constraints).
Scénario : Un workflow a été mis en place pour la soumission et la validation des projets dans une entreprise. Les responsables de projet peuvent soumettre des propositions de projets et chaque proposition doit être validée par un manager. Le manager peut approuver ou refuser la proposition (dans un temps imparti). Dans le cas d’une approbation, l’équipe proposée avec le projet est notifiée et le projet est créé dans le système de l’entreprise. Dans le cas contraire, le projet est rejeté et son créateur est notifié.
Voici comment les Azure Fonctions sont organisées :
Commençons par mettre en place notre fonction cliente :
Ensuite, nous avons notre Fonction d’orchestration :
Note : nous utilisons l’instance ID de l’orchestrateur comme ID de proposition
Ici, nous mettons en attente notre orchestrateur avec
WaitForExternalEvent
le temps qu’un manager approuve/rejette la proposition.
Note : un timeout de 30 minutes a été mis en place. Ainsi, si aucun manager n’a donné de réponse dans les 30 prochaines minutes, la proposition est automatiquement rejetée.
Si la proposition est approuvée, l’équipe assignée à la proposition est notifiée et le projet est créé dans le système de l’entreprise.
Approbation/rejet proposition :
La totalité du code peut être retrouvée sur le repo git suivant : https://github.com/anthonyalliot/az-durable-function
Comment tester : il suffit d’ouvrir le projet dans Visual Studio et de le lancer en mode debug. Trois fonctions peuvent être déclenchées via une requête http :
Méthode HTTP |
Nom fonction |
Description |
GET/POST | run | Lance le workflow (retourne l’ID de la proposition) |
POST | approve-proposal/{proposalId} | Approuver une proposition |
POST | reject-proposal/{proposalId} | Rejeter une proposition |
N’hésitez pas à mettre des points d’arrêts dans les Orchestrateurs pour mieux comprendre leur fonctionnement.
Ci-dessus le retour de la fonction run nous fournit l’ID de la proposition et nous permet, entre autres, de suivre le statut de la fonction, d’arrêter son exécution, etc.
Pour aller plus loin sur les Fonctions Durables
On constate que les Fonctions Durables proposent de multiples possibilités et l’implémentation d’un workflow reste relativement directe.
Cependant, le code de notre exemple ci-dessus reste très procédural : de ce fait, dans des scénarios beaucoup plus complexes, ces Azures Fonctions peuvent rapidement devenir illisibles et inmaintenables. Il faudra donc passer un peu plus de temps sur la conception et ne pas hésiter à coupler les Azure Fonctions avec d’autres services Azure tels que les Logic Apps.
Pour aller plus loin : deux sujets n’ont pas été approfondis/traités dans cet article et méritent qu’on s’y intéresse :
- Les Modèles d’application: très utiles pour vous aider à concevoir/implémenter vos Azure Fonctions (dans mon scénario, j’ai utilisé les modèles chaînage de fonctions, fan-in/fan-out, API HTTP Async et interaction humaine) ;
- Les Entités Durables: elles permettent d’ajouter un état à vos fonctions (ça pourrait faire l’objet d’un autre article 😉)
Pour les personnes qui ne sont toujours pas convaincues par les Azure Fonctions : comment auriez-vous implémenté le scénario ci-dessus dans une Web App ? 🙂