Exercício – adicionar código para monitorizar e registar o progresso do trabalho do Azure Batch na sua aplicação
A API de cliente do Batch permite que uma aplicação monitorize o estado atual de conjuntos, nós, trabalhos e tarefas.
Para concluir o aplicativo de console da sua empresa para converter vídeos, você deseja que o aplicativo monitore e relate o status das conversões de arquivos. Também deve reduzir os custos provenientes do Batch ao adicionar a capacidade da aplicação de eliminar os trabalhos e o conjunto assim que os vídeos forem convertidos. Para reduzir os custos de armazenamento de ficheiros, também deveria remover os ficheiros de vídeo carregados.
O aplicativo verifica se há um pool existente e cria um se ele não existir. O trabalho e as tarefas são iniciados e, em seguida, monitorados. Depois que as tarefas forem concluídas com êxito, o aplicativo apresenta a opção de excluir o trabalho e o pool criados. O aplicativo exclui automaticamente os vídeos carregados para economizar nos custos de armazenamento de blob.
Adicionar monitorização
No Cloud Shell, edite o ficheiro
Program.cs
no editor:code Program.cs
Adicione o seguinte método,
MonitorTasksAsync()
, a Program.cs para monitorizar as tarefas do trabalho.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; }
O objeto
TaskStateMonitor
é chamado e será devolvido quando o estado de todas as tarefas for (TaskState.Completed
). A aplicação irá exceder o limite de tempo se tiver de aguardar mais do que o valortimeout
.Este método utiliza o
batchClient.JobOperations.ListTasks
para obter o estado atual das tarefas na conta do Batch. Podemos filtrar essas chamadas para retornar apenas as informações de que precisamos passando umODATADetailLevel
parâmetro. Uma vez que todas as tarefas tenham sido concluídas, o código precisa verificar se todas elas foram concluídas com êxito, portanto, usar um filtro de"executionInfo/result eq 'Failure'"
com aListTasks
chamada retorna todas as tarefas que falharam.Adicione uma chamada ao novo método
MonitorTasksAsync()
dentro do bloco using noMain()
e armazene a lista de tarefas devolvida da chamadaAddTaskAsync
.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)); }
Limpeza
Dentro do bloco de uso, abaixo da chamada para a
MonitorTasks
chamada de método, adicione este código de limpeza.// 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."); }
O código anterior exclui o contêiner de entrada, que contém todos os vídeos carregados.
Em seguida, o terminal solicita que o usuário opte por excluir o trabalho e o pool. O
batchClient
permite que o aplicativo exclua esses componentes.
Testar a aplicação de consola
No editor de códigos, clique com o botão direito do mouse e selecione Salvar e, em seguida, selecione Sair.
Compile e execute a aplicação.
dotnet run
Você deve obter uma saída semelhante à seguinte:
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...
No entanto, o aplicativo falhará se o trabalho da execução anterior ainda existir, porque o aplicativo anterior não tinha o código de limpeza do trabalho. Pode eliminar o trabalho no portal do Azure ou no Cloud Shell com:
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):
No prompt, digite y.
A aplicação de consola será executada muito mais rapidamente desta vez, pois os nós (três máquinas virtuais a executar o Windows 2012) estarão inativos e a aguardar que um trabalho seja executado.
Gorjeta
Há uma versão totalmente comentada e funcional do aplicativo no GitHub.