Sdílet prostřednictvím


Scénář monitorování v Durable Functions – ukázka sledovacího programu počasí

Vzor monitorování odkazuje na flexibilní opakovaný proces v pracovním postupu – například dotazování, dokud nebudou splněny určité podmínky. Tento článek vysvětluje ukázku, která k implementaci monitorování používá Durable Functions .

Požadavky

Přehled scénáře

Tato ukázka monitoruje aktuální povětrnostní podmínky polohy a upozorní uživatele pomocí SMS, když jsou oblohy jasné. K kontrole počasí a odesílání výstrah můžete použít běžnou funkci aktivovanou časovačem. Jedním z problémů s tímto přístupem je správa životnosti. Pokud by se měla odeslat jenom jedna výstraha, monitorování se musí po zjištění jasného počasí vypnout. Model monitorování může mimo jiné ukončit vlastní spuštění:

  • Monitoruje spouštění v intervalech, nikoli v plánech: aktivační událost časovače se spouští každou hodinu. Monitorování čeká jednu hodinu mezi akcemi . Akce monitorování se nepřekrývají, pokud nejsou zadané, což může být důležité pro dlouhotrvající úlohy.
  • Monitorování můžou mít dynamické intervaly: doba čekání se může změnit na základě určité podmínky.
  • Monitorování se může ukončit, když je splněna některá podmínka nebo ukončena jiným procesem.
  • Monitory mohou přijímat parametry. Ukázka ukazuje, jak lze stejný proces monitorování počasí použít na libovolné požadované místo a telefonní číslo.
  • Monitory jsou škálovatelné. Vzhledem k tomu, že každý monitor je instancí orchestrace, lze vytvořit více monitorů, aniž by bylo nutné vytvářet nové funkce nebo definovat více kódu.
  • Monitory se snadno integrují do větších pracovních postupů. Monitorování může být jedna část složitější funkce orchestrace nebo dílčí orchestrace.

Konfigurace

Konfigurace integrace Twilio

Tato ukázka zahrnuje použití služby Twilio k odesílání sms zpráv na mobilní telefon. Azure Functions už podporuje Twilio prostřednictvím vazby Twilio a ukázka tuto funkci používá.

První věc, kterou potřebujete, je účet Twilio. Můžete vytvořit jednu zdarma na https://www.twilio.com/try-twilio. Jakmile budete mít účet, přidejte do aplikace funkcí následující tři nastavení aplikace.

Název nastavení aplikace Popis hodnoty
TwilioAccountSid Identifikátor SID pro váš účet Twilio
TwilioAuthToken Ověřovací token pro váš účet Twilio
TwilioPhoneNumber Telefonní číslo přidružené k vašemu účtu Twilio. Slouží k odesílání zpráv SMS.

Konfigurace integrace Weather Underground

Tato ukázka zahrnuje použití rozhraní API Weather Underground ke kontrole aktuálních povětrnostních podmínek pro umístění.

První věc, kterou potřebujete, je účet Weather Underground. Můžete si ho zdarma vytvořit na https://www.wunderground.com/signupadrese . Jakmile máte účet, musíte získat klíč rozhraní API. Můžete to udělat tak https://www.wunderground.com/weather/api, že navštívíte a vyberete Nastavení klíče. Plán Stratus Developer je bezplatný a dostatečný ke spuštění této ukázky.

Jakmile budete mít klíč rozhraní API, přidejte do aplikace funkcí následující nastavení aplikace.

Název nastavení aplikace Popis hodnoty
WeatherUndergroundApiKey Váš klíč rozhraní API Weather Underground.

Funkce

Tento článek vysvětluje následující funkce v ukázkové aplikaci:

  • E3_Monitor: Funkce orchestrátoru, která pravidelně voláE3_GetIsClear. E3_SendGoodWeatherAlert Zavolá, pokud E3_GetIsClear vrátí hodnotu true.
  • E3_GetIsClear: Funkce aktivity, která kontroluje aktuální povětrnostní podmínky pro umístění.
  • E3_SendGoodWeatherAlert: Funkce aktivity, která odesílá zprávu SMS přes Twilio.

funkce orchestrátoru E3_Monitor

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

Orchestrátor vyžaduje, aby byl monitor a telefonní číslo, aby poslal zprávu, když se počasí v místě vyjasní. Tato data se předávají orchestrátoru jako objekt silného typu MonitorRequest .

Tato funkce orchestrátoru provádí následující akce:

  1. Získá MonitorRequest skládající se z umístění pro monitorování a telefonní číslo, na které odesílá SMS oznámení.
  2. Určuje dobu vypršení platnosti monitoru. Ukázka používá pevně zakódovanou hodnotu pro stručnost.
  3. Zavolá E3_GetIsClear , aby zjistili, jestli jsou v požadované lokalitě jasné skiy.
  4. Pokud je počasí jasné, zavolá E3_SendGoodWeatherAlert poslat sms oznámení na požadované telefonní číslo.
  5. Vytvoří trvalý časovač pro obnovení orchestrace v dalším intervalu dotazování. Ukázka používá pevně zakódovanou hodnotu pro stručnost.
  6. Poběží až do doby, kdy aktuální čas UTC překročí dobu vypršení platnosti monitorování, nebo se odešle upozornění SMS.

Více instancí orchestrátoru může běžet současně voláním funkce orchestrátoru několikrát. Je možné zadat umístění, které chcete monitorovat, a telefonní číslo, na které se má odeslat upozornění SMS. Nakonec si všimněte, že funkce orchestrátoru není spuštěná při čekání na časovač, takže se vám za ni nebudou účtovat poplatky.

funkce aktivity E3_GetIsClear

Stejně jako u jiných ukázek jsou pomocné funkce aktivit běžnými funkcemi, které používají vazbu triggeru activityTrigger . Funkce E3_GetIsClear získá aktuální povětrnostní podmínky pomocí rozhraní API Weather Underground a určí, zda je obloha jasná.

[FunctionName("E3_GetIsClear")]
public static async Task<bool> GetIsClear([ActivityTrigger] Location location)
{
    var currentConditions = await WeatherUnderground.GetCurrentConditionsAsync(location);
    return currentConditions.Equals(WeatherCondition.Clear);
}

funkce aktivity E3_SendGoodWeatherAlert

Funkce E3_SendGoodWeatherAlert používá vazbu Twilio k odeslání zprávy SMS s oznámením koncovému uživateli, že je vhodná doba na procházku.

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

Poznámka:

Abyste mohli spustit vzorový kód, budete muset nainstalovat Microsoft.Azure.WebJobs.Extensions.Twilio balíček NuGet.

Spuštění ukázky

Pomocí funkcí aktivovaných protokolem HTTP, které jsou součástí ukázky, můžete orchestraci spustit odesláním následujícího požadavku 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}"}

Instance E3_Monitor se spustí a dotazuje aktuální povětrnostní podmínky požadovaného umístění. Pokud je počasí jasné, zavolá funkci aktivity, která odešle výstrahu; v opačném případě nastaví časovač. Po vypršení platnosti časovače se orchestrace obnoví.

Aktivitu orchestrace můžete zobrazit tak, že se podíváte na protokoly funkcí na portálu Azure Functions.

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)

Orchestrace se dokončí, jakmile dojde k vypršení časového limitu nebo se zjistí jasná obloha. Můžete také použít terminate rozhraní API uvnitř jiné funkce nebo vyvolat webhook HTTP POST terminatePostUri odkazovaný v předchozí odpovědi 202. Pokud chcete webhook použít, nahraďte {text} důvodem předčasného ukončení. Adresa URL HTTP POST vypadá přibližně takto:

POST https://{host}/runtime/webhooks/durabletask/instances/f6893f25acf64df2ab53a35c09d52635/terminate?reason=Because&taskHub=SampleHubVS&connection=Storage&code={systemKey}

Další kroky

Tato ukázka ukazuje, jak pomocí Durable Functions monitorovat stav externího zdroje pomocí trvalých časovačů a podmíněné logiky. Další ukázka ukazuje, jak pomocí externích událostí a trvalých časovačů zpracovat lidskou interakci.