Esercizio: Aggiungere codice per monitorare e registrare l'avanzamento del processo di Azure Batch nell'app

Completato

L'API client di Batch consente a un'app di monitorare lo stato corrente di pool, nodi, processi e attività.

Per completare l'app console aziendale per convertire video, è necessario che l'app monitori e segnali lo stato delle conversioni di file. È anche necessario ridurre i costi per l'uso di Batch, consentendo all'app di eliminare i processi e i pool dopo che i video sono stati convertiti. Per ridurre i costi di archiviazione dei file, è anche necessario rimuovere i file video caricati.

L'app verifica l'esistenza di un pool e, se non esiste, ne crea uno. Il processo e le attività vengono avviati, quindi monitorati. Al completamento delle attività, l'app presenta l'opzione per eliminare il processo e il pool creati. L'app elimina automaticamente i video caricati per risparmiare sui costi di archiviazione BLOB.

Aggiungere il monitoraggio

  1. In Cloud Shell modificare il file Program.cs nell'editor:

    code Program.cs
    
  2. Aggiungere il metodo MonitorTasksAsync() seguente a Program.cs per monitorare le attività del processo.

    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;
    }
    

    Verrà chiamato l'oggettoTaskStateMonitor, che sarà restituito quando lo stato di tutte le attività sarà (TaskState.Completed). Si verificherà il timeout dell'app se resterà in attesa più del valore specificato da timeout.

    Questo metodo usa batchClient.JobOperations.ListTasks per ottenere lo stato corrente delle attività nell'account Batch. È possibile filtrare queste chiamate per restituire solo le informazioni necessarie, passando un parametro ODATADetailLevel. Dopo il completamento di tutte le attività, il codice deve verificare che siano state tutte completate correttamente, quindi l'uso di un filtro di "executionInfo/result eq 'Failure'" con la chiamata ListTasks restituisce tutte le attività non riuscite.

  3. Aggiungere una chiamata al nuovo metodo MonitorTasksAsync() all'interno del blocco using in Main() e archiviare l'elenco di attività restituito dalla chiamata 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));
    }
    

Eseguire la pulizia

  1. All'interno del blocco using, sotto la chiamata al metodo MonitorTasks, aggiungere questo codice di pulizia.

    // 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.");
    }
    

    Il codice precedente elimina il contenitore di input, in cui sono contenuti tutti i video caricati.

    Il terminale chiede quindi all'utente di scegliere di eliminare il processo e il pool. batchClient consente all'app di eliminare questi componenti.

Testare l'app console

  1. Nell'editor di codice fare clic con il pulsante destro del mouse, scegliere Salva e quindi selezionare Esci.

  2. Compilare ed eseguire l'app.

    dotnet run
    
  3. Dovrebbe essere visualizzato un output simile al seguente:

    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. L'app avrà tuttavia esito negativo se il processo dell'esecuzione precedente esiste ancora, perché l'app precedente non aveva codice di pulizia del processo. È possibile eliminare il processo nel portale di Azure o in Cloud Shell con il codice seguente:

    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):
    

    Quando viene visualizzata la richiesta, digitare y.

  5. L'app console sarà eseguita molto più rapidamente questa volta poiché i nodi, vale a dire le tre macchine virtuali che eseguono Windows 2012, saranno inattivi e in attesa di un processo da eseguire.