Ejercicio: Adición de código para supervisar y registrar el progreso del trabajo de Azure Batch en la aplicación

Completado

La API cliente de Batch permite que una aplicación supervise el estado actual de grupos, nodos, trabajos y tareas.

Para completar la aplicación de consola de la empresa para convertir vídeos, desea tener el monitor de la aplicación y el informe sobre el estado de las conversiones de archivos. Además, para reducir los costos en los que incurrirá la instancia de Batch, le gustaría que la aplicación pudiese eliminar los trabajos y el grupo una vez que se hayan convertido los vídeos. Para reducir los costos de almacenamiento de archivos, también le gustaría quitar los archivos de vídeo cargados.

La aplicación comprobará si existe un grupo y creará uno si no existiera. Se iniciarán el trabajo y las tareas y, a continuación, se supervisarán. Una vez que las tareas se hayan completado correctamente, la aplicación ofrecerá la opción de eliminar el trabajo y el grupo creados. La aplicación eliminará de forma automática los vídeos cargados para ahorrar costes de almacenamiento de blobs.

Adición de supervisión

  1. En Cloud Shell, modifique el archivo Program.cs en el editor:

    code Program.cs
    
  2. Agregue el método siguiente, MonitorTasksAsync(), a Program.cs para supervisar las tareas del trabajo.

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

    Se llama al objeto TaskStateMonitor y se devolverá cuando el estado de todas las tareas sea (TaskState.Completed). La aplicación agotará el tiempo de espera si tiene que esperar más de lo indicado por el valor timeout.

    En este método se usa batchClient.JobOperations.ListTasks para obtener el estado actual de las tareas en la cuenta de Batch. Se pueden filtrar estas llamadas para devolver únicamente la información que se necesite al pasar un parámetro ODATADetailLevel. Una vez completadas todas las tareas, el código deberá comprobar que todos se completaron correctamente, por lo que el uso de un filtro de "executionInfo/result eq 'Failure'" con la llamada ListTasks devolverá todas las tareas que no se pudieron realizar.

  3. Agregue una llamada al nuevo método MonitorTasksAsync() dentro del bloque using de Main() y almacene la lista de tareas devuelta desde la llamada a 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));
    }
    

Limpiar

  1. Dentro del bloque de uso, debajo de la llamada al método MonitorTasks, agregue este código de limpieza.

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

    El código anterior elimina el contenedor de entrada, que contiene todos los vídeos cargados.

    Después, el terminal pedirá al usuario que elija si se eliminarán el trabajo y el grupo. El batchClient permitirá que la aplicación elimine estos componentes.

Prueba de la aplicación de consola

  1. En el editor de código, haga clic con el botón derecho y seleccione Guardar; a continuación, seleccione Salir.

  2. Compile y ejecute la aplicación.

    dotnet run
    
  3. Debería obtener una salida similar a la siguiente:

    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. Sin embargo, se producirá un error en la aplicación si el trabajo de la ejecución anterior siguiera existiendo, ya que la aplicación anterior no tenía el código de limpieza del trabajo. Puede eliminar el trabajo en Azure Portal o en Cloud Shell con:

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

    En el símbolo del sistema, escriba y.

  5. En esta ocasión, la aplicación de consola se ejecutará puesto que los nodos (tres máquinas de virtuales que ejecutan Windows 2012) estarán inactivos y esperando a que se ejecute un trabajo.