Exercice - Ajouter du code pour superviser et journaliser la progression de travaux Azure Batch dans votre application

Effectué

L’API du client Batch permet à une application de superviser l’état actuel de pools, de nœuds, de travaux et de tâches.

Pour compléter l’application console de votre entreprise permettant de convertir des vidéos, vous voulez que l’application surveille et signale l’état des conversions de fichiers. Vous souhaitez également réduire les coûts engendrés par votre travail Batch en ajoutant la possibilité pour l’application de supprimer les travaux et le pool une fois les vidéos converties. Pour réduire les coûts de stockage de fichiers, vous souhaitez également supprimer les fichiers vidéo chargés.

L’application vérifie la présence d’un pool existant et en crée un s’il n’existe pas. Le travail et les tâches sont démarrés, puis surveillées. Une fois les tâches terminées correctement, l’application donne la possibilité de supprimer le travail et le pool créés. L’application supprime automatiquement les vidéos chargées pour réduire les coûts de stockage des blobs.

Ajouter une fonctionnalité de supervision

  1. Dans Cloud Shell, modifiez le fichier Program.cs dans l’éditeur :

    code Program.cs
    
  2. Ajoutez la méthode suivante, MonitorTasksAsync(), à Program.cs pour superviser les tâches du travail.

    private static async Task<bool> MonitorTasksAsync(BatchClient batchClient, string jobId, TimeSpan timeout)
    {
        bool allTasksSuccessful = true;
        const string completeMessage = "All tasks reached state Completed.";
        const string incompleteMessage = "One or more tasks failed to reach the Completed state within the timeout period.";
        const string successMessage = "Success! All tasks completed successfully. Output files uploaded to output container.";
        const string failureMessage = "One or more tasks failed.";
    
        Console.WriteLine("Monitoring all tasks for 'Completed' state, timeout in {0}...", timeout.ToString());
    
        // We use a TaskStateMonitor to monitor the state of our tasks. In this case, we will wait for all tasks to
        // reach the Completed state.
        IEnumerable<CloudTask> addedTasks = batchClient.JobOperations.ListTasks(JobId);
    
        TaskStateMonitor taskStateMonitor = batchClient.Utilities.CreateTaskStateMonitor();
        try
        {
            await taskStateMonitor.WhenAll(addedTasks, TaskState.Completed, timeout);
        }
        catch (TimeoutException)
        {
            await batchClient.JobOperations.TerminateJobAsync(jobId);
            Console.WriteLine(incompleteMessage);
            return false;
        }
        await batchClient.JobOperations.TerminateJobAsync(jobId);
        Console.WriteLine(completeMessage);
    
        // All tasks have reached the "Completed" state, however, this does not guarantee all tasks completed successfully.
        // Here we further check for any tasks with an execution result of "Failure".
    
        // Obtain the collection of tasks currently managed by the job. 
        // Use a detail level to specify that only the "id" property of each task should be populated. 
        // See https://learn.microsoft.com/azure/batch/batch-efficient-list-queries
        ODATADetailLevel detail = new ODATADetailLevel(selectClause: "executionInfo");
    
        // Filter for tasks with 'Failure' result.
        detail.FilterClause = "executionInfo/result eq 'Failure'";
    
        List<CloudTask> failedTasks = await batchClient.JobOperations.ListTasks(jobId, detail).ToListAsync();
    
        if (failedTasks.Any())
        {
            allTasksSuccessful = false;
            Console.WriteLine(failureMessage);
        }
        else
        {
            Console.WriteLine(successMessage);
        }
        return allTasksSuccessful;
    }
    

    L’objet TaskStateMonitor est appelé et retourné quand l’état de toutes les tâches est TaskState.Completed. L’application expire si elle doit attendre plus longtemps que la valeur timeout.

    Cette méthode utilise batchClient.JobOperations.ListTasks pour obtenir l’état actuel des tâches sur le compte Batch. Vous pouvez filtrer ces appels de façon à retourner seulement les informations dont vous avez besoin en passant un paramètre ODATADetailLevel. Une fois que toutes les tâches ont été effectuées, le code doit vérifier qu’elles se sont toutes terminées correctement, en utilisant un filtre "executionInfo/result eq 'Failure'" avec l’appel ListTasks qui retourne toutes les tâches qui ont échoué.

  3. Ajoutez un appel à la nouvelle méthode MonitorTasksAsync() à l’intérieur du bloc using dans Main(), puis stockez la liste des tâches retournée par l’appel AddTaskAsync.

    using (BatchClient batchClient = BatchClient.Open(sharedKeyCredentials))
    {
        // Create the Batch pool, which contains the compute nodes that execute the tasks.
        await CreatePoolIfNotExistAsync(batchClient, PoolId);
    
        // Create the job that runs the tasks.
        await CreateJobAsync(batchClient, JobId, PoolId);
    
        // Create a collection of tasks and add them to the Batch job. 
        // Provide a shared access signature for the tasks so that they can upload their output
        // to the Storage container.
        List<CloudTask> runningTasks = await AddTasksAsync(batchClient, JobId, inputFiles, outputContainerSasUrl);
    
        // Monitor task success or failure, specifying a maximum amount of time to wait for
        // the tasks to complete.
        await MonitorTasksAsync(batchClient, JobId, TimeSpan.FromMinutes(30));
    }
    

Nettoyage

  1. Sous l’appel de méthode MonitorTasks, dans le bloc Using, ajoutez ce code de nettoyage.

    // Delete input container in storage
    Console.WriteLine("Deleting container [{0}]...", inputContainerName);
    CloudBlobContainer container = blobClient.GetContainerReference(inputContainerName);
    await container.DeleteIfExistsAsync();
    
    // Clean up the job (if the user so chooses)
    Console.WriteLine();
    Console.Write("Delete job? [yes] no: ");
    string response = Console.ReadLine().ToLower();
    if (response != "n" && response != "no")
    {
        await batchClient.JobOperations.DeleteJobAsync(JobId);
    }
    
    // Clean up the pool (if the user so chooses - do not delete the pool if new batches of videos are ready to process)
    Console.Write("Delete pool? [yes] no: ");
    response = Console.ReadLine().ToLower();
    if (response != "n" && response != "no")
    {
        Console.WriteLine("Deleting pool ...");
        await batchClient.PoolOperations.DeletePoolAsync(PoolId);
        Console.WriteLine("Pool deleted.");
    }
    

    Le code ci-dessus supprime le conteneur d’entrée, qui contient toutes les vidéos chargées.

    Le terminal demande ensuite à l’utilisateur s’il veut supprimer le travail et le pool. Le batchClient permet à l’application de supprimer ces composants.

Tester l’application console

  1. Dans l’éditeur de code, cliquez avec le bouton de droite et sélectionnez Enregistrer, puis Quitter.

  2. Générez et exécutez l’application.

    dotnet run
    
  3. Vous devez obtenir une sortie qui ressemble à ceci :

    Creating container [input].
    Creating container [output].
    Uploading file ~\cutifypets\InputFiles\3.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\2.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\4.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\1.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\5.mp4 to container [input]...
    Uploading file ~\cutifypets\InputFiles\6.mp4 to container [input]...
    Creating pool [WinFFmpegPool]...
    Creating job [WinFFmpegJob]...
    Adding 6 tasks to job [WinFFmpegJob]...
    Monitoring all tasks for 'Completed' state, timeout in 00:30:00...
    All tasks reached state Completed.
    Success! All tasks completed successfully. Output files uploaded to output container.
    Deleting container [input]...
    
    Delete job? [yes] no: y
    Delete pool? [yes] no: y
    
    Sample complete, hit ENTER to exit...
    
  4. Cependant, l’application va échouer si le travail de l’exécution précédente existe encore, car l’application précédente n’avait par le code permettant de nettoyer le travail. Vous pouvez supprimer le travail dans le portail Azure ou dans Cloud Shell avec :

    az batch job delete --job-id WinFFmpegJob \
    --account-name $BATCH_NAME \
    --account-key $BATCH_KEY \
    --account-endpoint $BATCH_URL
    
    Are you sure you want to perform this operation? (y/n):
    

    À l’invite, tapez y.

  5. L’application console s’exécute beaucoup plus rapidement cette fois dans la mesure où les nœuds (trois machines virtuelles exécutant Windows 2012) sont inactifs et attendent qu’un travail s’exécute.