Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O padrão de monitorização é um processo recorrente num fluxo de trabalho que sonda um sistema externo até que uma condição seja cumprida — por exemplo, verificar o estado do trabalho até a sua conclusão, ou observar dados meteorológicos até o céu estar limpo. Ao contrário de um temporizador com agendamento fixo, um monitor aguarda entre as iterações (evitando sobreposição), suporta intervalos dinâmicos e pode encerrar-se assim que a condição é satisfeita ou expira um tempo limite.
Este artigo explica como implementar o padrão do monitor usando orquestrações duráveis.
Sugestão
Este artigo mostra a implementação completa. Para uma visão conceptual dos casos de uso de orquestração durável, veja O que é a Tarefa Durável?.
Os exemplos de Durable Functions incluem um cenário de monitorização meteorológica (C#/JavaScript) e um cenário de monitorização de problemas no GitHub (Python).
Observação
A versão 4 do modelo de programação Node.js para o Funções do Azure está geralmente disponível. O modelo v4 foi concebido para proporcionar uma experiência mais flexível e intuitiva para programadores JavaScript e TypeScript. Para mais informações sobre as diferenças entre v3 e v4, consulte o guia de migração.
Nos seguintes excertos de código, o JavaScript (PM4) denota o modelo de programação v4, a nova experiência.
O exemplo dos SDKs de Tarefas Duráveis demonstra monitorização do estado do trabalho com intervalos de sondagem configuráveis usando .NET, JavaScript, Python e Java.
Pré-requisitos
- .NET 8.0 SDK ou posterior
- Acesso ao Azure Durable Task Scheduler ou ao emulador local
Visão geral do cenário de monitorização
Este exemplo monitora as condições meteorológicas atuais de um local e alerta um usuário por SMS quando o céu está limpo. Você pode usar uma função regular acionada pelo temporizador para verificar o tempo e enviar alertas. No entanto, um problema desta abordagem é a gestão ao longo da vida. Se apenas um alerta deve ser enviado, o monitor precisa se desativar depois que o tempo claro for detetado.
O padrão de monitoramento pode encerrar sua própria execução, entre outros benefícios:
- Os monitores funcionam em intervalos, não em horários: um temporizador aciona a cada hora; Um monitor espera uma hora entre as ações. As ações de um monitor não irão sobrepor-se, a menos que especificado de outra forma, o que pode ser importante para tarefas de longa duração.
- Os monitores podem ter intervalos dinâmicos: o tempo de espera pode mudar com base em alguma condição.
- Os monitores podem ser encerrados quando alguma condição for atendida ou ser encerrados por outro processo.
- Os monitores podem ter parâmetros. O exemplo mostra como o mesmo processo de monitorização pode ser aplicado a qualquer localização, número de telefone ou repositório solicitado.
- Os monitores são escaláveis. Como cada monitor é uma instância de orquestração, vários monitores podem ser criados sem a necessidade de criar novas funções ou definir mais código.
- Os monitores integram-se facilmente em fluxos de trabalho maiores. Um monitor pode ser uma secção de uma função de orquestração mais complexa, ou uma suborquestração.
Esta amostra monitoriza o estado de um trabalho de longa duração e devolve o resultado final quando o trabalho termina ou expira. Poderia usar um ciclo de sondagens regular para verificar o estado do trabalho, mas esta abordagem tem limitações na gestão do tempo de vida e fiabilidade.
O padrão de monitorização oferece os seguintes benefícios:
- Sondagem duradoura: A orquestração sobrevive aos reinícios do processo e pode continuar a monitorização mesmo após a ocorrência de falhas.
- Intervalos configuráveis: O tempo de espera entre verificações de estado pode ser ajustado dinamicamente.
- Suporte de timeout: O monitor pode terminar quando uma condição é cumprida ou quando expira um timeout.
- Visibilidade de estado: Os clientes podem consultar o estado personalizado da orquestração para ver o progresso atual da monitorização.
- Escalabilidade: Vários monitores podem funcionar em simultâneo, cada um a acompanhar diferentes tarefas.
Configuração
Configurando a integração do Twilio
Este exemplo envolve o uso do serviço Twilio para enviar mensagens SMS para um telefone celular. Funções do Azure já tem suporte para Twilio através da ligação Twilio, e o exemplo usa essa funcionalidade.
A primeira coisa que você precisa é de uma conta Twilio. Você pode criar um gratuitamente em https://www.twilio.com/try-twilio. Depois de ter uma conta, adicione as três configurações de aplicativo a seguir ao seu aplicativo de função.
| Nome de definição de aplicação | Descrição do valor |
|---|---|
| TwilioAccountSid | O SID da sua conta Twilio |
| TwilioAuthToken | O token de autenticação para sua conta Twilio |
| TwilioPhoneNumber | O número de telefone associado à sua conta Twilio. Isso é usado para enviar mensagens SMS. |
Configuração de uma API meteorológica
Os exemplos de C#/JavaScript chamam uma API meteorológica para verificar as condições atuais. Precisa de fornecer a sua própria chave de API meteorológica e atualizar o código de exemplo em conformidade. O código de exemplo refere-se a uma WeatherUndergroundApiKey definição de aplicação — substitua-a pela chave do fornecedor meteorológico escolhido.
| Nome de definição de aplicação | Descrição do valor |
|---|---|
| WeatherUndergroundApiKey | A tua chave API meteorológica (substitui pelo nome da chave do teu fornecedor conforme necessário). |
Orchestrator
[FunctionName("E3_Monitor")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext monitorContext, ILogger log)
{
MonitorRequest input = monitorContext.GetInput<MonitorRequest>();
if (!monitorContext.IsReplaying) { log.LogInformation($"Received monitor request. Location: {input?.Location}. Phone: {input?.Phone}."); }
VerifyRequest(input);
DateTime endTime = monitorContext.CurrentUtcDateTime.AddHours(6);
if (!monitorContext.IsReplaying) { log.LogInformation($"Instantiating monitor for {input.Location}. Expires: {endTime}."); }
while (monitorContext.CurrentUtcDateTime < endTime)
{
// Check the weather
if (!monitorContext.IsReplaying) { log.LogInformation($"Checking current weather conditions for {input.Location} at {monitorContext.CurrentUtcDateTime}."); }
bool isClear = await monitorContext.CallActivityAsync<bool>("E3_GetIsClear", input.Location);
if (isClear)
{
// It's not raining! Or snowing. Or misting. Tell our user to take advantage of it.
if (!monitorContext.IsReplaying) { log.LogInformation($"Detected clear weather for {input.Location}. Notifying {input.Phone}."); }
await monitorContext.CallActivityAsync("E3_SendGoodWeatherAlert", input.Phone);
break;
}
else
{
// Wait for the next checkpoint
var nextCheckpoint = monitorContext.CurrentUtcDateTime.AddMinutes(30);
if (!monitorContext.IsReplaying) { log.LogInformation($"Next check for {input.Location} at {nextCheckpoint}."); }
await monitorContext.CreateTimer(nextCheckpoint, CancellationToken.None);
}
}
log.LogInformation($"Monitor expiring.");
}
[Deterministic]
private static void VerifyRequest(MonitorRequest request)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request), "An input object is required.");
}
if (request.Location == null)
{
throw new ArgumentNullException(nameof(request.Location), "A location input is required.");
}
if (string.IsNullOrEmpty(request.Phone))
{
throw new ArgumentNullException(nameof(request.Phone), "A phone number input is required.");
}
}
A função de orquestrador requer um local para monitorizar e um número de telefone para enviar uma mensagem quando o tempo se torna claro nesse local. Estes dados são passados para a função do orquestrador como um objeto fortemente tipado MonitorRequest .
Esta função orquestradora executa as seguintes ações:
- Recebe o
MonitorRequest que consiste nolocalização a monitorizar e otelefone para o qual envia uma notificação SMS (ou repo para o Python exemplo). - Determina o tempo de expiração do monitor. O exemplo usa um valor codificado para brevidade.
- Chama a atividade de verificação de estado para determinar se a condição está cumprida.
- Se a condição for cumprida, ativa a atividade de alerta para enviar uma notificação.
- Cria um temporizador durável para retomar a orquestração no próximo intervalo de sondagem. O exemplo usa um valor codificado para brevidade.
- Continua a funcionar até que a hora UTC atual passe o tempo de expiração do monitor, ou até que seja enviado um alerta.
Múltiplas instâncias de funções de orquestrador podem ser executadas simultaneamente ao chamar a função de orquestrador várias vezes. A localização a monitorizar e o número de telefone a enviar um alerta podem ser especificados. A função de orquestrador não está a correr enquanto se espera pelo temporizador, por isso não vais ser cobrado por isso.
O orquestrador verifica periodicamente o estado de uma tarefa e regressa quando a tarefa termina ou expira.
using Microsoft.DurableTask;
using System;
using System.Threading.Tasks;
[DurableTask(nameof(MonitoringJobOrchestration))]
public class MonitoringJobOrchestration : TaskOrchestrator<JobMonitorInput, JobMonitorResult>
{
public override async Task<JobMonitorResult> RunAsync(
TaskOrchestrationContext context, JobMonitorInput input)
{
var jobId = input.JobId;
var pollingInterval = TimeSpan.FromSeconds(input.PollingIntervalSeconds);
var expirationTime = context.CurrentUtcDateTime.AddSeconds(input.TimeoutSeconds);
// Initialize monitoring state
int checkCount = 0;
while (context.CurrentUtcDateTime < expirationTime)
{
// Check current job status
var jobStatus = await context.CallActivityAsync<JobStatus>(
nameof(CheckJobStatusActivity),
new CheckJobInput { JobId = jobId, CheckCount = checkCount });
checkCount = jobStatus.CheckCount;
// Make job status available via custom status
context.SetCustomStatus(jobStatus);
if (jobStatus.Status == "Completed")
{
return new JobMonitorResult
{
JobId = jobId,
FinalStatus = "Completed",
ChecksPerformed = checkCount
};
}
// Calculate next check time
var nextCheck = context.CurrentUtcDateTime.Add(pollingInterval);
if (nextCheck > expirationTime)
{
nextCheck = expirationTime;
}
// Wait until next polling interval
await context.CreateTimer(nextCheck, default);
}
// Timeout reached
return new JobMonitorResult
{
JobId = jobId,
FinalStatus = "Timeout",
ChecksPerformed = checkCount
};
}
}
Este orquestrador executa as seguintes ações:
- Toma o ID do trabalho, o intervalo de sondagem e o timeout como parâmetros de entrada.
- Regista a hora de início e calcula a data de validade.
- Entra num ciclo de sondagens que verifica o estado do trabalho.
- Atualiza o estado personalizado para que os clientes possam monitorizar o progresso.
- Se o trabalho for concluído, devolve o resultado final.
- Se o tempo limite for atingido, devolve um estado de tempo limite.
- Utiliza
CreateTimerpara aguardar entre tentativas de sondagem sem consumir recursos.
Activities
Tal como noutros exemplos, as funções de atividade auxiliar são funções regulares que usam a activityTrigger ligação de gatilho.
Atividade de verificação de estado
A função E3_GetIsClear obtém as condições meteorológicas atuais usando a API Weather Underground e determina se o céu está limpo.
[FunctionName("E3_GetIsClear")]
public static async Task<bool> GetIsClear([ActivityTrigger] Location location)
{
var currentConditions = await WeatherUnderground.GetCurrentConditionsAsync(location);
return currentConditions.Equals(WeatherCondition.Clear);
}
Atividade de alerta
A função E3_SendGoodWeatherAlert usa a ligação Twilio para enviar uma mensagem SMS a notificar o utilizador final de que é um bom momento para um passeio.
[FunctionName("E3_SendGoodWeatherAlert")]
public static void SendGoodWeatherAlert(
[ActivityTrigger] string phoneNumber,
ILogger log,
[TwilioSms(AccountSidSetting = "TwilioAccountSid", AuthTokenSetting = "TwilioAuthToken", From = "%TwilioPhoneNumber%")]
out CreateMessageOptions message)
{
message = new CreateMessageOptions(new PhoneNumber(phoneNumber));
message.Body = $"The weather's clear outside! Go take a walk!";
}
internal class WeatherUnderground
{
private static readonly HttpClient httpClient = new HttpClient();
private static IReadOnlyDictionary<string, WeatherCondition> weatherMapping = new Dictionary<string, WeatherCondition>()
{
{ "Clear", WeatherCondition.Clear },
{ "Overcast", WeatherCondition.Clear },
{ "Cloudy", WeatherCondition.Clear },
{ "Clouds", WeatherCondition.Clear },
{ "Drizzle", WeatherCondition.Precipitation },
{ "Hail", WeatherCondition.Precipitation },
{ "Ice", WeatherCondition.Precipitation },
{ "Mist", WeatherCondition.Precipitation },
{ "Precipitation", WeatherCondition.Precipitation },
{ "Rain", WeatherCondition.Precipitation },
{ "Showers", WeatherCondition.Precipitation },
{ "Snow", WeatherCondition.Precipitation },
{ "Spray", WeatherCondition.Precipitation },
{ "Squall", WeatherCondition.Precipitation },
{ "Thunderstorm", WeatherCondition.Precipitation },
};
internal static async Task<WeatherCondition> GetCurrentConditionsAsync(Location location)
{
var apiKey = Environment.GetEnvironmentVariable("WeatherUndergroundApiKey");
if (string.IsNullOrEmpty(apiKey))
{
throw new InvalidOperationException("The WeatherUndergroundApiKey environment variable was not set.");
}
var callString = string.Format("http://api.wunderground.com/api/{0}/conditions/q/{1}/{2}.json", apiKey, location.State, location.City);
var response = await httpClient.GetAsync(callString);
var conditions = await response.Content.ReadAsAsync<JObject>();
JToken currentObservation;
if (!conditions.TryGetValue("current_observation", out currentObservation))
{
JToken error = conditions.SelectToken("response.error");
if (error != null)
{
throw new InvalidOperationException($"API returned an error: {error}.");
}
else
{
throw new ArgumentException("Could not find weather for this location. Try being more specific.");
}
}
return MapToWeatherCondition((string)(currentObservation as JObject).GetValue("weather"));
}
private static WeatherCondition MapToWeatherCondition(string weather)
{
foreach (var pair in weatherMapping)
{
if (weather.Contains(pair.Key))
{
return pair.Value;
}
}
return WeatherCondition.Other;
}
}
Observação
Terás de instalar o pacote Nuget Microsoft.Azure.WebJobs.Extensions.Twilio para executar o código de exemplo.
A atividade verifica o estado atual do trabalho. Numa aplicação real, isto chamaria uma API ou serviço externo.
using Microsoft.DurableTask;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
[DurableTask(nameof(CheckJobStatusActivity))]
public class CheckJobStatusActivity : TaskActivity<CheckJobInput, JobStatus>
{
private readonly ILogger<CheckJobStatusActivity> _logger;
public CheckJobStatusActivity(ILogger<CheckJobStatusActivity> logger)
{
_logger = logger;
}
public override Task<JobStatus> RunAsync(TaskActivityContext context, CheckJobInput input)
{
_logger.LogInformation("Checking status for job: {JobId} (check #{CheckCount})",
input.JobId, input.CheckCount + 1);
// Simulate job status - completes after 3 checks
var status = input.CheckCount >= 3 ? "Completed" : "Running";
return Task.FromResult(new JobStatus
{
JobId = input.JobId,
Status = status,
CheckCount = input.CheckCount + 1,
LastCheckTime = DateTime.UtcNow
});
}
}
// Data classes
public class JobMonitorInput
{
public string JobId { get; set; }
public int PollingIntervalSeconds { get; set; } = 5;
public int TimeoutSeconds { get; set; } = 30;
}
public class CheckJobInput
{
public string JobId { get; set; }
public int CheckCount { get; set; }
}
public class JobStatus
{
public string JobId { get; set; }
public string Status { get; set; }
public int CheckCount { get; set; }
public DateTime LastCheckTime { get; set; }
}
public class JobMonitorResult
{
public string JobId { get; set; }
public string FinalStatus { get; set; }
public int ChecksPerformed { get; set; }
}
Executar o exemplo de monitor
Usando as funções acionadas por HTTP incluídas no exemplo, você pode iniciar a orquestração enviando a seguinte solicitação HTTP POST:
POST https://{host}/orchestrators/E3_Monitor
Content-Length: 77
Content-Type: application/json
{ "location": { "city": "Redmond", "state": "WA" }, "phone": "+1425XXXXXXX" }
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8
Location: https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635?taskHub=SampleHubVS&connection=Storage&code={SystemKey}
RetryAfter: 10
{"id": "f6893f25acf64df2ab53a35c09d52635", "statusQueryGetUri": "https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635?taskHub=SampleHubVS&connection=Storage&code={systemKey}", "sendEventPostUri": "https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635/raiseEvent/{eventName}?taskHub=SampleHubVS&connection=Storage&code={systemKey}", "terminatePostUri": "https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635/terminate?reason={text}&taskHub=SampleHubVS&connection=Storage&code={systemKey}"}
A instância E3_Monitor inicia e consulta as condições atuais. Se a condição for cumprida, chama uma função de atividade para enviar um alerta; caso contrário, define um temporizador. Quando o temporizador expira, a orquestração é retomada.
Pode ver a atividade da orquestração consultando os registos de funções no portal Funções do Azure.
A orquestração termina assim que o seu limite de tempo é atingido ou a condição é detetada. Também pode usar a terminate API dentro de outra função ou invocar o webhook HTTP POST terminatePostUri referenciado na resposta 202 anterior. Para usar o webhook, substitua {text} pelo motivo da rescisão antecipada. O URL HTTP POST tem aproximadamente a seguinte aparência:
POST https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635/terminate?reason=Because&taskHub=SampleHubVS&connection=Storage&code={systemKey}
Para analisar a amostra, precisa de:
Inicie o emulador Durable Task Scheduler (para desenvolvimento local):
docker run -d -p 8080:8080 -p 8082:8082 --name dts-emulator mcr.microsoft.com/dts/dts-emulator:latestInicie o worker para registar o orquestrador e as atividades.
Execute o cliente para agendar uma orquestração de monitoramento.
using System;
using System.Threading.Tasks;
var client = DurableTaskClientBuilder.UseDurableTaskScheduler(connectionString).Build();
// Schedule the monitoring orchestration
var input = new JobMonitorInput
{
JobId = "job-" + Guid.NewGuid().ToString(),
PollingIntervalSeconds = 5,
TimeoutSeconds = 30
};
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(MonitoringJobOrchestration), input);
Console.WriteLine($"Started monitoring orchestration: {instanceId}");
// Wait for completion while checking status
while (true)
{
var state = await client.GetInstanceMetadataAsync(instanceId, getInputsAndOutputs: true);
if (state.RuntimeStatus == OrchestrationRuntimeStatus.Completed ||
state.RuntimeStatus == OrchestrationRuntimeStatus.Failed)
{
Console.WriteLine($"Monitoring completed: {state.ReadOutputAs<JobMonitorResult>().FinalStatus}");
break;
}
Console.WriteLine($"Current status: {state.ReadCustomStatusAs<JobStatus>()?.Status}");
await Task.Delay(2000);
}
Passos seguintes
Este exemplo demonstra como usar Durable Functions para monitorizar o estado de uma fonte externa usando temporizadores duradouros e lógica condicional. O exemplo seguinte mostra como usar eventos externos e temporizadores duráveis para lidar com a interação humana.
Este exemplo demonstrou como usar os SDKs de Tarefas Duráveis para implementar o padrão de monitorização com temporizadores duráveis e rastreamento de estado. Saiba mais sobre outros padrões e características.