Övervaka scenario i Durable Functions – Väderbevakarexempel
Övervakningsmönstret refererar till en flexibel återkommande process i ett arbetsflöde , till exempel avsökning tills vissa villkor uppfylls. I den här artikeln beskrivs ett exempel som använder Durable Functions för att implementera övervakning.
Förutsättningar
Scenarioöversikt
Det här exemplet övervakar en plats aktuella väderförhållanden och varnar en användare via SMS när himlen är klar. Du kan använda en vanlig timerutlöst funktion för att kontrollera vädret och skicka aviseringar. Ett problem med den här metoden är dock livslängdshantering. Om endast en avisering ska skickas måste övervakaren inaktivera sig själv när klart väder har identifierats. Övervakningsmönstret kan bland annat avsluta sin egen körning:
- Övervakare körs med intervall, inte scheman: en timerutlösare körs varje timme, en övervakare väntar en timme mellan åtgärderna. En övervakarens åtgärder överlappar inte om de inte anges, vilket kan vara viktigt för långvariga uppgifter.
- Övervakare kan ha dynamiska intervall: väntetiden kan ändras baserat på vissa villkor.
- Övervakare kan avslutas när vissa villkor uppfylls eller avslutas av en annan process.
- Övervakare kan ta parametrar. Exemplet visar hur samma väderövervakningsprocess kan tillämpas på alla begärda platser och telefonnummer.
- Övervakare är skalbara. Eftersom varje övervakare är en orkestreringsinstans kan flera övervakare skapas utan att behöva skapa nya funktioner eller definiera mer kod.
- Övervakare integreras enkelt i större arbetsflöden. En övervakare kan vara ett avsnitt i en mer komplex orkestreringsfunktion eller en underorkestrering.
Konfiguration
Konfigurera Twilio-integrering
Det här exemplet omfattar användning av Twilio-tjänsten för att skicka SMS till en mobiltelefon. Azure Functions har redan stöd för Twilio via Twilio-bindningen och exemplet använder den funktionen.
Det första du behöver är ett Twilio-konto. Du kan skapa en kostnadsfri på https://www.twilio.com/try-twilio. När du har ett konto lägger du till följande tre appinställningar i funktionsappen.
Namn på appinställning | Värdebeskrivning |
---|---|
TwilioAccountSid | SID för ditt Twilio-konto |
TwilioAuthToken | Autentiseringstoken för ditt Twilio-konto |
TwilioPhoneNumber | Det telefonnummer som är kopplat till ditt Twilio-konto. Detta används för att skicka SMS. |
Konfigurera weather underground-integrering
Det här exemplet omfattar användning av API:et Weather Underground för att kontrollera aktuella väderförhållanden för en plats.
Det första du behöver är ett Weather Underground-konto. Du kan skapa en kostnadsfritt på https://www.wunderground.com/signup. När du har ett konto måste du skaffa en API-nyckel. Du kan göra det genom att besöka https://www.wunderground.com/weather/apioch sedan välja Nyckelinställningar. Stratus Developer-planen är kostnadsfri och tillräcklig för att köra det här exemplet.
När du har en API-nyckel lägger du till följande appinställning i funktionsappen.
Namn på appinställning | Värdebeskrivning |
---|---|
WeatherUndergroundApiKey | Din Weather Underground API-nyckel. |
Funktionerna
I den här artikeln beskrivs följande funktioner i exempelappen:
E3_Monitor
: En orkestreringsfunktion som anroparE3_GetIsClear
regelbundet. Den anroparE3_SendGoodWeatherAlert
omE3_GetIsClear
returnerar sant.E3_GetIsClear
: En aktivitetsfunktion som kontrollerar aktuella väderförhållanden för en plats.E3_SendGoodWeatherAlert
: En aktivitetsfunktion som skickar ett SMS via Twilio.
E3_Monitor orchestrator-funktion
[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.");
}
}
Orkestratorn kräver en plats att övervaka och ett telefonnummer för att skicka ett meddelande till när vädret blir klart på platsen. Dessa data skickas till orchestrator som ett starkt skrivet MonitorRequest
objekt.
Den här orkestreringsfunktionen utför följande åtgärder:
- Hämtar MonitorRequest som består av den plats som ska övervakas och det telefonnummer som den skickar ett SMS-meddelande till.
- Avgör förfallotiden för övervakaren. Exemplet använder ett hårdkodat värde för korthet.
- Anropar E3_GetIsClear för att avgöra om det finns klar himmel på den begärda platsen.
- Om vädret är klart ringer E3_SendGoodWeatherAlert för att skicka ett SMS-meddelande till det begärda telefonnumret.
- Skapar en varaktig timer för att återuppta orkestreringen vid nästa avsökningsintervall. Exemplet använder ett hårdkodat värde för korthet.
- Fortsätter att köras tills den aktuella UTC-tiden passerar övervakarens förfallotid, eller så skickas en SMS-avisering.
Flera orchestrator-instanser kan köras samtidigt genom att anropa orchestrator-funktionen flera gånger. Den plats som ska övervakas och det telefonnummer som du vill skicka en SMS-avisering till kan anges. Observera slutligen att orkestreringsfunktionen inte körs* medan du väntar på timern, så du debiteras inte för den.
E3_GetIsClear aktivitetsfunktion
Precis som med andra exempel är hjälpaktivitetsfunktionerna vanliga funktioner som använder utlösarbindningen activityTrigger
. Funktionen E3_GetIsClear hämtar de aktuella väderförhållandena med hjälp av Weather Underground-API:et och avgör om himlen är klar.
[FunctionName("E3_GetIsClear")]
public static async Task<bool> GetIsClear([ActivityTrigger] Location location)
{
var currentConditions = await WeatherUnderground.GetCurrentConditionsAsync(location);
return currentConditions.Equals(WeatherCondition.Clear);
}
E3_SendGoodWeatherAlert aktivitetsfunktion
Funktionen E3_SendGoodWeatherAlert använder Twilio-bindningen för att skicka ett SMS som meddelar slutanvändaren att det är en bra tid för en promenad.
[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;
}
}
Kommentar
Du måste installera Microsoft.Azure.WebJobs.Extensions.Twilio
Nuget-paketet för att köra exempelkoden.
Kör exemplet
Med hjälp av de HTTP-utlösta funktionerna i exemplet kan du starta orkestreringen genom att skicka följande HTTP POST-begäran:
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}"}
Den E3_Monitor instansen startar och frågar efter aktuella väderförhållanden för den begärda platsen. Om vädret är klart anropas en aktivitetsfunktion för att skicka en avisering. annars anges en timer. När timern upphör att gälla återupptas orkestreringen.
Du kan se orkestreringens aktivitet genom att titta på funktionsloggarna i Azure Functions-portalen.
2018-03-01T01:14:41.649 Function started (Id=2d5fcadf-275b-4226-a174-f9f943c90cd1)
2018-03-01T01:14:42.741 Started orchestration with ID = '1608200bb2ce4b7face5fc3b8e674f2e'.
2018-03-01T01:14:42.780 Function completed (Success, Id=2d5fcadf-275b-4226-a174-f9f943c90cd1, Duration=1111ms)
2018-03-01T01:14:52.765 Function started (Id=b1b7eb4a-96d3-4f11-a0ff-893e08dd4cfb)
2018-03-01T01:14:52.890 Received monitor request. Location: Redmond, WA. Phone: +1425XXXXXXX.
2018-03-01T01:14:52.895 Instantiating monitor for Redmond, WA. Expires: 3/1/2018 7:14:52 AM.
2018-03-01T01:14:52.909 Checking current weather conditions for Redmond, WA at 3/1/2018 1:14:52 AM.
2018-03-01T01:14:52.954 Function completed (Success, Id=b1b7eb4a-96d3-4f11-a0ff-893e08dd4cfb, Duration=189ms)
2018-03-01T01:14:53.226 Function started (Id=80a4cb26-c4be-46ba-85c8-ea0c6d07d859)
2018-03-01T01:14:53.808 Function completed (Success, Id=80a4cb26-c4be-46ba-85c8-ea0c6d07d859, Duration=582ms)
2018-03-01T01:14:53.967 Function started (Id=561d0c78-ee6e-46cb-b6db-39ef639c9a2c)
2018-03-01T01:14:53.996 Next check for Redmond, WA at 3/1/2018 1:44:53 AM.
2018-03-01T01:14:54.030 Function completed (Success, Id=561d0c78-ee6e-46cb-b6db-39ef639c9a2c, Duration=62ms)
Orkestreringen har slutförts när tidsgränsen nås eller klar himmel har identifierats. Du kan också använda API:et terminate
i en annan funktion eller anropa webhooken terminatePostUri HTTP POST som refereras i föregående 202-svar. Om du vill använda webhooken ersätter du {text}
med orsaken till den tidiga avslutningen. HTTP POST-URL:en ser ungefär ut så här:
POST https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635/terminate?reason=Because&taskHub=SampleHubVS&connection=Storage&code={systemKey}
Nästa steg
Det här exemplet visar hur du använder Durable Functions för att övervaka statusen för en extern källa med hjälp av varaktiga timers och villkorsstyrd logik. Nästa exempel visar hur du använder externa händelser och varaktiga timers för att hantera mänsklig interaktion.