Упражнение. Добавление кода для мониторинга и ведения журналов заданий пакетной службы Azure в приложении

Завершено

Клиентский API пакетной службы позволяет приложению отслеживать текущее состояние пулов, узлов, заданий и задач.

Чтобы завершить преобразование видео в консольное приложение вашей компании, необходимо иметь монитор приложения и сообщить о состоянии преобразования файлов. Вы также хотите снизить расходы на пакетную службу, добавив в приложение возможность удалять задания и пул после преобразования видео. Чтобы сократить затраты на хранение файлов, вы также хотите удалять переданные видеофайлы.

Приложение проверяет наличие существующего пула и создает его, если он не существует. Задание и задачи запускаются, а затем отслеживаются. После успешного завершения задач приложение предоставляет возможность удаления созданного задания и пула. Приложение автоматически удаляет загруженные видео, чтобы сэкономить на затратах на хранилище BLOB-объектов.

Добавление мониторинга

  1. В Cloud Shell измените файл Program.cs в редакторе:

    code Program.cs
    
  2. Добавьте следующий метод MonitorTasksAsync() в Program.cs, чтобы отслеживать задачи задания.

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

    Объект TaskStateMonitor вызывается и вернется, когда все задачи будут иметь состояние (TaskState.Completed). Время ожидания в приложении истечет через timeout.

    Этот метод использует batchClient.JobOperations.ListTasks для получения текущего состояния задач в учетной записи пакетной службы. Эти вызовы можно отфильтровать, чтобы возвращать только необходимые сведения, передав ODATADetailLevel параметр. После завершения всех задач код должен убедиться, что все они успешно завершены, поэтому при использовании фильтра "executionInfo/result eq 'Failure'" с вызовом возвращаются все задачи, которые завершились сбоем ListTasks .

  3. Добавьте вызов в новый метод MonitorTasksAsync() внутри блока using в Main() и сохраните список задач, возвращенный вызовом 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));
    }
    

Очистка

  1. В блоке using под вызовом MonitorTasks метода добавьте этот код очистки.

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

    Предыдущий код удаляет входной контейнер, содержащий все отправленные видео.

    Затем терминал предложит пользователю удалить задание и пул. Приложение batchClient позволяет удалить эти компоненты.

Тестирование консольного приложения

  1. В редакторе кода щелкните правой кнопкой и выберите Сохранить, а затем нажмите Выход.

  2. Выполните сборку и запустите приложение.

    dotnet run
    
  3. Выходные данные должны выглядеть примерно так:

    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. Однако приложение завершится ошибкой, если задание из предыдущего запуска по-прежнему существует, так как предыдущее приложение не было кода очистки задания. Вы можете удалить задание на портале Azure или в Cloud Shell с помощью команды:

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

    В командной строке введите y.

  5. На этот раз консольное приложение будет выполняться быстрее, так как узлы (три виртуальные машины под управлением Windows 2012) будут работать в режиме ожидания выполнения задания.

Совет

На сайте GitHub есть полностью закомментированная и рабочая версия приложения.