web-dev-qa-db-fra.com

Comment exécuter de nombreuses fonctions durables déclenchées par Azure Queue?

En bref, notre tâche consiste à traiter un grand nombre de messages d'entrée. Pour résoudre ce problème, nous avons décidé d'utiliser Azure Queue Storage et Azure Functions. Nous avons une structure Azure Functions similaire au code suivant:

Fonction déclenchée en file d'attente:

[FunctionName("MessageControllerExecutor")]
public static async void Run(
    [QueueTrigger(QUEUE_NAME, Connection = QUEUE_CONNECTION_NAME)]string queueMessage,
    [OrchestrationClient] DurableOrchestrationClient client,
    TraceWriter log)
{
    await client.StartNewAsync("MessageController", queueMessage);
}

Fonction durable:

[FunctionName("MessageController")]
public static async void Run(
    [OrchestrationTrigger] DurableOrchestrationContext context,
    TraceWriter log)
{
    if (!context.IsReplaying) log.Warning("MessageController started");

    var function1ResultTask = context.CallActivityAsync<ResultMessage>("Function_1", new InputMessage());
    var function2ResultTask = context.CallActivityAsync<ResultMessage>("Function_2", new InputMessage());

    await Task.WhenAll(function1ResultTask, function2ResultTask);

    // process Function_1 and Function_2 results
    // ...
}

Exemple de fonction d'activité simple:

[FunctionName("Function_1")]
public static ResultMessage Run(
    [ActivityTrigger] DurableActivityContext activityContext,
    TraceWriter log)
{
    var msg = activityContext.GetInput<InputMessage>();
    int time = new Random().Next(1, 3);
    Thread.Sleep(time * 1000);

    return new ResultMessage()
    {
        Payload = $"Function_1 slept for {time} sec"
    };
}

MessageControllerExecutor est déclenché lorsqu'un nouvel élément est reçu dans une file d'attente. MessageController est une fonction durable qui utilise peu de fonctions d'activité simples pour traiter chaque message.

Lorsque nous transmettons des messages à la file d'attente, les fonctions MessageControllerExecutor démarrent immédiatement et de manière asynchrone, déclenchent MessageController et transmettent le message afin qu'il fonctionne comme prévu. Mais nous sommes confrontés au problème que toutes les fonctions de MessageController ne sont pas exécutées. Par exemple, nous avons mis 100 messages dans la file d'attente, mais seulement 10 à 20% des messages ont été traités par MessageController. Certains messages n'ont pas été traités ou ont été traités avec un long délai. Il semble que les fonctions durables n'aient pas démarré, bien qu'aucune exception n'ait été lancée.

Nous avons donc quelques questions:

  1. Cette solution avec des fonctions durables déclenchées par une file d'attente est-elle correcte pour traiter la file d'attente des messages ou existe-t-il un meilleur moyen de déclencher des fonctions durables par la file d'attente?
  2. Existe-t-il des limitations pour exécuter des fonctions durables?
  3. Combien de fonctions durables peuvent être exécutées simultanément?
9
Taras Trofymchuk
  1. Oui, c’est un moyen tout à fait valable de lancer des orchestrations!
  2. Bien sûr, voici quelques détails sur l’architecture en ce qui concerne les performances et l’évolutivité .
  3. Je pense que vous avez probablement l'intention de demander ici est la suivante: combien d'occurrences d'orchestration d'une même définition de fonction durable peuvent être exécutées en même temps? C'est en effet un aspect très important à comprendre. Les fonctions d'orchestration sont elles-mêmes des threads simples et, pour ce lien à l'échelle que je vous ai donné ci-dessus, sont équilibrées dans un ensemble de files d'attente de contrôle. Vous pouvez lire le document pour plus d'informations, mais le résultat final est que vous ne souhaitez pas effectuer aucun travail autre que la véritable orchestration dans votre fonction d'orchestration, car il s'agit de votre limite d'évolutivité. Ce sont les fonctions orchestration action qui se comportent comme toute autre fonction Azure et qui n’ont pratiquement aucune limite quant à leur évolutivité.

Dans la question ci-dessus, vous avez éliminé du code de votre déclencheur d’orchestration dans un souci de concision que je comprends, mais que faites-vous exactement après la await Task.WhenAll(...)? Si cela inclut tout type de traitement important, vous devriez vraiment le confier à une troisième fonction d'action (par exemple, Function_3) à faire, puis renvoyer simplement les résultats de la fonction d'orchestration.

Mise à jour: Je viens de remarquer que vos fonctions sont définies par async void. Si je devais deviner, cela poserait effectivement un problème pour l'exécution. Pouvez-vous essayer de le changer en async Task et voir si votre problème disparaît? En règle générale définir des méthodes comme async void est mal vu dans .NET .

5
Drew Marsh

Une extension pour la réponse de Drew. Vous ne devez pas utiliser Thread.Sleep (), car la documentation states utilise à la place CreateTimer Api.

1
SayusiAndo