Hubs de tâches dans Fonctions durables (Azure Functions)
Un hub de tâches dans Durable Functions est une représentation de l’état actuel de l’application dans le stockage, y compris tout le travail en attente. Pendant qu’une application de fonction est en cours d’exécution, la progression des fonctions d’orchestration, d’activité et d’entité est continuellement stockée dans le hub de tâches. Cela garantit que l’application peut reprendre le traitement là où elle s’est arrêtée, si elle doit être redémarrée après avoir été temporairement arrêtée ou interrompue pour une raison quelconque. En outre, cela permet à l’application de fonction de mettre à l’échelle les rôles de travail de calcul dynamiquement.
Conceptuellement, un hub de tâches stocke les informations suivantes :
- Les états d’instance de toutes les instances d’orchestration et d’entité.
- Les messages à traiter, y compris
- tous les messages d’activité qui représentent les activités en attente d’exécution :
- tous les messages d’instance en attente d’être livrés aux instances.
La différence entre les messages d’activité et d’instance est que les messages d’activité sont sans état et peuvent donc être traités n’importe où, tandis que les messages d’instance doivent être remis à une instance avec état spécifique (orchestration ou entité), identifiée par son ID d’instance.
En interne, chaque fournisseur de stockage peut utiliser une organisation différente pour représenter les états d’instance et les messages. Par exemple, les messages sont stockés dans des files d’attente de stockage Azure par le fournisseur de Stockage Azure, mais dans des tables relationnelles par le fournisseur MSSQL. Ces différences n’ont pas d’importance en ce qui concerne la conception de l’application, mais certaines d’entre elles peuvent influencer les caractéristiques de performances. Nous les abordons dans la section Représentation dans le stockage ci-dessous.
Éléments de travail
Les messages d’activité et les messages d’instance dans le hub de tâches représentent le travail que l’application de fonction doit traiter. Pendant que l’application de fonction est en cours d’exécution, elle extrait en continu des éléments de travail à partir du hub de tâches. Chaque élément de travail traite un ou plusieurs messages. Nous faisons la distinction entre deux types d’éléments de travail :
- Éléments de travail d’activité : exécutez une fonction d’activité pour traiter un message d’activité.
- Éléments de travail d’orchestrateur : exécutez une fonction d’orchestrateur ou d’entité pour traiter un ou plusieurs messages d’instance.
Les rôles de travail peuvent traiter plusieurs éléments de travail en même temps, soumis aux limites concurrentielles configurées par worker.
Une fois qu’un worker a terminé un élément de travail, il valide les effets dans le hub de tâches. Ces effets varient selon le type de fonction exécuté :
- Une fonction d’activité terminée crée un message d’instance contenant le résultat, adressé à l’instance d’orchestrateur parent.
- Une fonction d’orchestrateur terminée met à jour l’état et l’historique de l’orchestration, et peut créer des messages.
- Une fonction d’entité terminée met à jour l’état de l’entité et peut également créer des messages d’instance.
Pour les orchestrations, chaque élément de travail représente un épisode de l’exécution de cette orchestration. Un épisode commence lorsqu’il existe de nouveaux messages que l’orchestrateur doit traiter. Un tel message peut indiquer que l’orchestration doit démarrer, ou il peut indiquer qu’une activité, un appel d’entité, un retardateur ou une sous-orchestration se sont terminés, ou il peut représenter un événement externe. Le message déclenche un élément de travail qui permet à l’orchestrateur de traiter le résultat et de continuer avec l’épisode suivant. Cet épisode se termine lorsque l’orchestrateur se termine ou atteint un point où il doit attendre de nouveaux messages.
Exemple d'exécution
Considérez une orchestration Fan-out-Fan-in qui démarre deux activités en parallèle et attend que les deux se terminent :
[FunctionName("Example")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
Task t1 = context.CallActivityAsync<int>("MyActivity", 1);
Task t2 = context.CallActivityAsync<int>("MyActivity", 2);
await Task.WhenAll(t1, t2);
}
Une fois cette orchestration lancée par un client, elle est traitée par l’application de fonction comme séquence d’éléments de travail. Chaque élément de travail terminé met à jour l’état du hub de tâches lors de sa validation. Voici les étapes :
Un client demande de démarrer une nouvelle orchestration avec l’ID d’instance « 123 ». Une fois que le client a terminé cette requête, le hub de tâches contient un espace réservé pour l’état d’orchestration et un message d’instance :
L’étiquette
ExecutionStarted
est l’un des nombreux types d’événements d’historique qui identifient les différents types de messages et d’événements participant à l’historique d’une orchestration.Un worker exécute un élément de travail d’orchestrateur pour traiter le message
ExecutionStarted
. Il appelle la fonction d’orchestrateur qui commence à exécuter le code d’orchestration. Ce code planifie deux activités, puis cesse de s’exécuter lorsqu’il attend les résultats. Une fois que le worker valide cet élément de travail, le hub de tâches contientL’état de runtime est maintenant
Running
, deux nouveaux messagesTaskScheduled
ont été ajoutés, et l’historique contient à présent les cinq événementsOrchestratorStarted
,ExecutionStarted
,TaskScheduled
,TaskScheduled
,OrchestratorCompleted
. Ces événements représentent le premier épisode de l’exécution de cette orchestration.Un worker exécute un élément de travail d’activité pour traiter l’un des messages
TaskScheduled
. Il appelle la fonction d’activité avec l’entrée « 2 ». Une fois la fonction d’activité terminée, cela crée un messageTaskCompleted
contenant le résultat. Une fois que le worker valide cet élément de travail, le hub de tâches contientUn worker exécute un élément de travail d’orchestrateur pour traiter le message
TaskCompleted
. Si l’orchestration est toujours mise en cache en mémoire, il peut simplement reprendre l’exécution. Sinon, le worker relit d’abord l’historique pour récupérer l’état actuel de l’orchestration. Ensuite, il poursuit l’orchestration, fournissant le résultat de l’activité. Après avoir reçu ce résultat, l’orchestration attend toujours le résultat de l’autre activité, de sorte qu’elle cesse de s’exécuter une fois de plus. Une fois que le worker valide cet élément de travail, le hub de tâches contientL’historique d’orchestration contient désormais trois autres événements
OrchestratorStarted
,TaskCompleted
,OrchestratorCompleted
. Ces événements représentent le deuxième épisode de l’exécution de cette orchestration.Un worker exécute un élément de travail d’activité pour traiter le message
TaskScheduled
restant. Il appelle la fonction d’activité avec l’entrée « 1 ». Une fois que le worker valide cet élément de travail, le hub de tâches contientUn worker exécute un autre élément de travail d’orchestrateur pour traiter le message
TaskCompleted
. Après avoir reçu ce deuxième résultat, l’orchestration se termine. Une fois que le worker valide cet élément de travail, le hub de tâches contientL’état du runtime est maintenant
Completed
, et l’historique d’orchestration contient à présent quatre autres événementsOrchestratorStarted
,TaskCompleted
,ExecutionCompleted
,OrchestratorCompleted
. Ces événements représentent le troisième et dernier épisode de l’exécution de cette orchestration.
L’historique final de l’exécution de cette orchestration contient ensuite les 12 événements OrchestratorStarted
, ExecutionStarted
, TaskScheduled
, TaskScheduled
, OrchestratorCompleted
, OrchestratorStarted
, TaskCompleted
, OrchestratorCompleted
, OrchestratorStarted
, TaskCompleted
, ExecutionCompleted
, OrchestratorCompleted
.
Notes
La planification affichée n’est pas la seule : il existe de nombreuses planifications possibles légèrement différentes. Par exemple, si la deuxième activité se termine plus tôt, les deux messages d’instance TaskCompleted
peuvent être traités par un seul élément de travail. Dans ce cas, l’historique d’exécution est un peu plus court, car il n’y a que deux épisodes, et il contient les 10 événements suivants : OrchestratorStarted
, ExecutionStarted
, TaskScheduled
, TaskScheduled
, OrchestratorCompleted
, OrchestratorStarted
, TaskCompleted
, TaskCompleted
, ExecutionCompleted
, OrchestratorCompleted
.
Gestion du hub de tâches
Examinons ensuite de plus près la façon dont les hubs de tâches sont créés ou supprimés, comment utiliser correctement les hubs de tâches lors de l’exécution de plusieurs applications de fonction et comment le contenu des hubs de tâches peut être inspecté.
Création et suppression
Un hub de tâches vide avec toutes les ressources requises est automatiquement créé dans le stockage lorsqu’une application de fonction est démarrée la première fois.
Si vous utilisez le fournisseur de Stockage Azure par défaut, aucune configuration supplémentaire n’est requise. Sinon, suivez les instructions de configuration des fournisseurs de stockage pour vous assurer que le fournisseur de stockage peut provisionner et accéder correctement aux ressources de stockage requises pour le hub de tâches.
Notes
Le hub de tâches n’est pas automatiquement supprimé lorsque vous arrêtez ou supprimez l’application de fonction. Vous devez supprimer manuellement le hub de tâches, son contenu ou le compte de stockage contenant si vous ne souhaitez plus conserver ces données.
Conseil
Dans un scénario de développement, vous devrez peut-être souvent redémarrer à partir d’un état propre. Pour ce faire rapidement, vous pouvez simplement modifier le nom du hub de tâches configuré. Cela force la création d’un hub de tâches vide lorsque vous redémarrez l’application. N’oubliez pas que les anciennes données ne sont pas supprimées dans ce cas.
Plusieurs applications de fonction
Si plusieurs applications de fonction partagent un compte de stockage, chaque application de fonction doit être configurée avec un nom de hub de tâches distinct. Cette exigence s’applique également aux emplacements intermédiaires : chaque emplacement intermédiaire doit être configuré avec un nom de hub de tâches unique. Un même compte de stockage peut comporter plusieurs hubs de tâches. Cette restriction s’applique généralement également aux autres fournisseurs de stockage.
Le diagramme suivant illustre un hub de tâches par application de fonction dans les comptes Stockage Azure partagés et dédiés.
Remarque
L’exception à la règle de partage de hub de tâches est si vous configurez votre application pour la reprise d’activité régionale. Pour plus d’informations, consultez l’article Reprise d’activité et géodistribution.
Inspection du contenu
Il existe plusieurs façons courantes d’inspecter le contenu d’un hub de tâches :
- Dans une application de fonction, l’objet client fournit des méthodes pour interroger le magasin d’instances. Pour en savoir plus sur les types de requêtes pris en charge, consultez l’article Gestion des instances.
- De même, l’API HTTP offre des requêtes REST pour interroger l’état des orchestrations et des entités. Pour plus d’informations, consultez Référence sur l’API HTTP.
- L’outil Durable Functions Monitor peut inspecter les hubs de tâches et offre différentes options pour l’affichage visuel.
Pour certains des fournisseurs de stockage, il est également possible d’inspecter le hub de tâches en accédant directement au stockage sous-jacent :
- Si vous utilisez le fournisseur de Stockage Azure, les états d’instance sont stockés dans la table d’instance et la table d’historique qui peuvent être inspectés à l’aide d’outils tels que Explorateur Stockage Azure.
- Si vous utilisez le fournisseur de stockage MSSQL, les requêtes et outils SQL peuvent être utilisés pour inspecter le contenu du hub de tâches à l’intérieur de la base de données.
Représentation dans le stockage
Chaque fournisseur de stockage utilise une organisation interne différente pour représenter les hubs de tâches dans le stockage. Comprendre cette organisation, bien qu’elle n’est pas requise, peut être utile lors de la résolution des problèmes d’une application de fonction ou lors de la tentative de garantir les performances, la scalabilité ou les cibles de coût. Nous expliquons ainsi brièvement, pour chaque fournisseur de stockage, comment les données sont organisées dans le stockage. Pour plus d’informations sur les différentes options du fournisseur de stockage et leur comparaison, consultez Fournisseurs de stockage Durable Functions.
Fournisseur Stockage Azure
Le fournisseur de Stockage Azure représente le hub de tâches dans le stockage à l’aide des composants suivants :
- Deux tables Azure stockent les états d’instance.
- Une file d’attente Azure stocke les messages d’activité.
- Une ou plusieurs files d’attente Azure stockent les messages d’instance. Chacune de ces dénommées files d’attente de contrôle représente une partition affectée à un sous-ensemble de tous les messages d’instance, en fonction du hachage de l’ID d’instance.
- Quelques conteneurs de blob supplémentaires utilisés pour les blobs de bail et/ou les messages volumineux.
Par exemple, un hub de tâches nommé xyz
avec PartitionCount = 4
contient les files d’attente et les tables suivantes :
Ensuite, nous décrivons ces composants et le rôle qu’ils jouent plus en détail.
Pour plus d’informations sur la façon dont les hubs de tâches sont représentés par le fournisseur de Stockage Azure, consultez la documentation du fournisseur de Stockage Azure.
Fournisseur de stockage Netherite
Netherite partitionne tous les états du hub de tâches en un nombre spécifié de partitions. Dans le stockage, les ressources suivantes sont utilisées :
- Un conteneur de blobs Stockage Azure qui contient tous les blobs, regroupés par partition.
- Une table Azure qui contient des métriques publiées sur les partitions.
- Un espace de noms Azure Event Hubs pour remettre des messages entre des partitions.
Par exemple, un hub de tâches nommé mytaskhub
avec PartitionCount = 32
est représenté dans le stockage comme suit :
Remarque
L’état du hub de tâches est stocké dans le conteneur de blobs x-storage
. La table DurableTaskPartitions
et l’espace de noms EventHubs contiennent des données redondantes : si leur contenu est perdu, ils peuvent être récupérés automatiquement. Par conséquent, il n’est pas nécessaire de configurer l’espace de noms Azure Event Hubs pour conserver les messages au-delà de l’heure d’expiration par défaut.
Netherite utilise un mécanisme d’approvisionnement d’événements, basé sur un journal et des points de contrôle, pour représenter l’état actuel d’une partition. Les objets blob de blocs et les objets blob de pages sont utilisés. Il n’est pas possible de lire ce format directement à partir du stockage, de sorte que l’application de fonction doit être en cours d’exécution lors de l’interrogation du magasin d’instances.
Pour plus d’informations sur les hubs de tâches pour le fournisseur de stockage Netherite, consultez Informations sur le hub de tâches pour le fournisseur de stockage Netherite.
Fournisseur de stockage MSSQL
Toutes les données du hub de tâches sont stockées dans une base de données relationnelle unique, à l’aide de plusieurs tables :
- Les tables
dt.Instances
etdt.History
stockent les états d’instance. - La table
dt.NewEvents
stocke les messages d’instance. - La table
dt.NewTasks
stocke les messages d’activité.
Pour permettre à plusieurs hubs de tâches de coexister indépendamment dans la même base de données, chaque table inclut une colonne TaskHub
dans le cadre de sa clé primaire. Contrairement aux deux autres fournisseurs, le fournisseur MSSQL n’a pas de concept de partitions.
Pour plus d’informations sur les hubs de tâches pour le fournisseur de stockage MSSQL, consultez Informations sur le hub de tâches pour le fournisseur de stockage Microsoft SQL (MSSQL).
Noms de hub de tâches
Les hubs de tâches sont identifiés par un nom qui doit être conforme aux règles suivantes :
- Nom uniquement composé de caractères alphanumériques
- Nom commençant par une lettre
- Nom compris entre 3 et 45 caractères
Le nom du hub de tâches est déclaré dans le fichier host.json, comme illustré dans l'exemple suivant :
host.json (Functions 2.0)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "MyTaskHub"
}
}
}
host.json (Functions 1.x)
{
"durableTask": {
"hubName": "MyTaskHub"
}
}
Les hubs de tâches peuvent également être configurés à l’aide de paramètres d’application, comme indiqué dans l’exemple de fichier host.json
suivant :
host.json (Functions 1.0)
{
"durableTask": {
"hubName": "%MyTaskHub%"
}
}
host.json (Functions 2.0)
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "%MyTaskHub%"
}
}
}
Le nom du hub de tâches est défini sur la valeur du paramètre d’application MyTaskHub
. Le fichier local.settings.json
suivant montre comment définir le paramètre MyTaskHub
sur samplehubname
:
{
"IsEncrypted": false,
"Values": {
"MyTaskHub" : "samplehubname"
}
}
Notes
Lors de l’utilisation d’emplacements de déploiement, c’est une bonne pratique que de configurer le nom du hub de tâches en utilisant des paramètres d’application. Si vous voulez qu’un emplacement particulier utilise toujours un hub de tâches particulier, utilisez des paramètres d’application « liés à l’emplacement ».
Outre host.json, les noms de hub de tâches peuvent également être configurés dans les métadonnées de liaison du client d’orchestration. Cela est utile si vous avez besoin d’accéder aux orchestrations ou aux entités qui résident dans une application de fonction distincte. Le code suivant montre comment écrire une fonction recourant à la liaison du client d’orchestration pour utiliser un hub de tâches configuré en tant que paramètre d’application :
[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[DurableClient(TaskHub = "%MyTaskHub%")] IDurableOrchestrationClient starter,
string functionName,
ILogger log)
{
// Function input comes from the request content.
object eventData = await req.Content.ReadAsAsync<object>();
string instanceId = await starter.StartNewAsync(functionName, eventData);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
Notes
Le précédent exemple porte sur Durable Functions 2.x. Pour Durable Functions 1.x, vous devez utiliser DurableOrchestrationContext
au lieu de IDurableOrchestrationContext
. Pour en savoir plus sur les différences entre les versions, consultez l’article Versions de Durable Functions.
Notes
La configuration de noms de hub de tâches dans les métadonnées de liaison du client est uniquement nécessaire lorsque vous utilisez une application de fonction pour accéder aux orchestrations et aux entités d’une autre application de fonction. Si les fonctions clientes sont définies dans la même application de fonction que les orchestrations et les entités, vous devez éviter de spécifier des noms de hub de tâches dans les métadonnées de liaison. Par défaut, toutes les liaisons clientes obtiennent leurs métadonnées de hub de tâches à partir des paramètres de host.json.
Les noms de hubs de tâches doivent commencer par une lettre et contenir uniquement des lettres et des chiffres. S’il n’est pas spécifié, un nom de hub de tâches par défaut sera utilisé comme indiqué dans le tableau suivant :
Version d’extension durable | Nom du hub de tâches par défaut |
---|---|
2.x | Lorsqu’il est déployé dans Azure, le nom du hub de tâches est dérivé du nom de l’application de fonction. En cas d’exécution en dehors d’Azure, le nom du hub de tâches par défaut est TestHubName . |
1.x | Le nom du hub de tâches par défaut pour tous les environnements est DurableFunctionsHub . |
Pour en savoir plus sur les différences entre les versions d’extension, consultez l’article Versions de Durable Functions.
Notes
Le nom permet de différencier les hubs de tâches les uns des autres lorsqu’ils sont plusieurs dans un compte de stockage partagé. Si plusieurs applications de fonction partagent un compte de stockage partagé, vous devez configurer explicitement des noms différents pour chaque hub de tâches dans les fichiers host.json. Dans le cas contraire, les applications de fonction seront en concurrence les unes avec les autres pour les messages, ce qui pourrait entraîner un comportement inhabituel, y compris des orchestrations qui se retrouveraient inopinément « bloquées » dans l’état Pending
ou Running
.