Impostare ed eseguire query sullo stato di orchestrazione personalizzato

Lo stato di orchestrazione personalizzato consente di collegare metadati JSON arbitrari a un'istanza di orchestrazione in esecuzione in modo che i client esterni possano eseguirne query in qualsiasi momento. Usare lo stato personalizzato quando è necessario:

  • Segnalare lo stato di avanzamento a metà del volo : consente a un'interfaccia utente di mostrare il passaggio raggiunto da un'orchestrazione senza attendere il completamento.
  • Restituisci dati dinamici ai chiamanti — visualizza suggerimenti, informazioni sullo sconto o istruzioni sui prossimi passaggi mentre l'orchestrazione è ancora in esecuzione.
  • Coordinarsi con i sistemi esterni — condividere lo stato che altri servizi o operatori umani possono monitorare e su cui possono agire.

Avvertimento

Il payload di stato personalizzato è limitato a 16 KB di testo JSON UTF-16. Se hai bisogno di un payload più grande, usa l'archiviazione esterna e memorizza un riferimento (ad esempio un Blob URL) nello stato personalizzato.

In Funzioni di Azure questo stato è disponibile tramite l'API HTTP GetStatus o l'API SDK equivalente nell'oggetto client di orchestrazione.

In Durable Task SDK questo stato è disponibile tramite le API di query sullo stato dell'orchestrazione nel DurableTaskClient (ad esempio, GetInstanceAsync in .NET o getInstanceMetadata in Java).

Importante

Attualmente, Durable Task SDK di PowerShell non è disponibile.

Casi d'uso di esempio per lo stato di orchestrazione personalizzato

La tabella seguente riepiloga i modelli comuni. Selezionare un caso d'uso per passare all'esempio corrispondente.

caso d'uso Descrizione
Visualizzare lo stato di orchestrazione Aggiornare una stringa o un oggetto dopo ogni attività in modo che i client possano visualizzare un indicatore di stato.
Restituire metadati dinamici ai client Impostare dati strutturati (ad esempio raccomandazioni) di cui i client eseguono il rendering senza che siano necessari endpoint lato server personalizzati.
Fornire dati interattivi ai client Mettere in evidenza gli URL di prenotazione, le informazioni sullo sconto o le istruzioni successive che i clienti seguono mentre l'orchestrazione attende un evento esterno.
Interrogare stato personalizzato Leggere il valore di stato personalizzato da un client usando le API HTTP o le chiamate SDK.

Visualizzare i progressi dell'orchestrazione

In questo modello, l'orchestratore chiama SetCustomStatus (o l'equivalente nella tua lingua) dopo il completamento di ogni attività, aggiornando lo stato con il nome dell'ultima città completata. Un client esegue il polling dell'endpoint di stato, ne legge il valore attuale e aggiorna un indicatore di avanzamento nell'interfaccia utente.

L'esempio seguente illustra lo stato di avanzamento della condivisione usando l'endpoint di stato HTTP Durable Functions:

Annotazioni

Questi esempi vengono scritti per Durable Functions 2.x e non sono compatibili con Durable Functions 1.x. Per altre informazioni sulle differenze tra le versioni, vedere l'articolo Durable Functions Versions.

[FunctionName("E1_HelloSequence")]
public static async Task<List<string>> Run(
    [OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var outputs = new List<string>();

    outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "Tokyo"));
    context.SetCustomStatus("Tokyo");
    outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "Seattle"));
    context.SetCustomStatus("Seattle");
    outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "London"));
    context.SetCustomStatus("London");

    // returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
    return outputs;
}

[FunctionName("E1_SayHello")]
public static string SayHello([ActivityTrigger] string name)
{
    return $"Hello {name}!";
}

L'esempio seguente illustra la condivisione dello stato di avanzamento usando le API client durable task SDK:

using System.Threading.Tasks;
using Microsoft.DurableTask;

public class HelloCities : TaskOrchestrator<object?, string>
{
    public override async Task<string> RunAsync(TaskOrchestrationContext context, object? input)
    {
        string result = "";

        result += await context.CallActivityAsync<string>("SayHello", "Tokyo") + ", ";
        context.SetCustomStatus("Tokyo");

        result += await context.CallActivityAsync<string>("SayHello", "London") + ", ";
        context.SetCustomStatus("London");

        result += await context.CallActivityAsync<string>("SayHello", "Seattle");
        context.SetCustomStatus("Seattle");

        return result;
    }
}

Il client può eseguire il polling dei metadati di orchestrazione e attendere che il CustomStatus campo sia impostato su "London":

using System.Threading.Tasks;
using Microsoft.DurableTask.Client;

string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("HelloCities");

OrchestrationMetadata metadata = await client.WaitForInstanceStartAsync(instanceId, getInputsAndOutputs: true);
while (metadata.SerializedCustomStatus is null || metadata.ReadCustomStatusAs<string>() != "London")
{
    await Task.Delay(200);
    metadata = await client.GetInstanceAsync(instanceId, getInputsAndOutputs: true) ?? metadata;
}

Il codice client seguente esegue il polling dello stato di orchestrazione e attende fino a quando CustomStatus non viene impostato su "London" prima di restituire una risposta:

[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
    [DurableClient] IDurableOrchestrationClient starter,
    string functionName,
    ILogger log)
{
    // Function input comes from the request content.
    dynamic eventData = await req.Content.ReadAsAsync<object>();
    string instanceId = await starter.StartNewAsync(functionName, (string)eventData);

    log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

    DurableOrchestrationStatus durableOrchestrationStatus = await starter.GetStatusAsync(instanceId);
    while (durableOrchestrationStatus.CustomStatus.ToString() != "London")
    {
        await Task.Delay(200);
        durableOrchestrationStatus = await starter.GetStatusAsync(instanceId);
    }

    HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new StringContent(JsonConvert.SerializeObject(durableOrchestrationStatus))
    };

    return httpResponseMessage;
  }
}

Restituire metadati dinamici ai client

È possibile usare lo stato di orchestrazione personalizzato per restituire dati strutturati, ad esempio raccomandazioni personalizzate, ai client senza creare endpoint separati. L'agente di orchestrazione imposta lo stato personalizzato in base all'input e il client lo legge tramite l'API di stato standard. In questo modo il codice lato client rimane generico, mentre tutta la logica rimane sul lato server.

[FunctionName("CityRecommender")]
public static void Run(
  [OrchestrationTrigger] IDurableOrchestrationContext context)
{
  int userChoice = context.GetInput<int>();

  switch (userChoice)
  {
    case 1:
    context.SetCustomStatus(new
    {
      recommendedCities = new[] {"Tokyo", "Seattle"},
      recommendedSeasons = new[] {"Spring", "Summer"}
     });
      break;
    case 2:
      context.SetCustomStatus(new
      {
                recommendedCities = new[] {"Seattle", "London"},
        recommendedSeasons = new[] {"Summer"}
      });
        break;
      case 3:
      context.SetCustomStatus(new
      {
                recommendedCities = new[] {"Tokyo", "London"},
        recommendedSeasons = new[] {"Spring", "Summer"}
      });
        break;
  }

  // Wait for user selection and refine the recommendation
}
using System.Threading.Tasks;
using Microsoft.DurableTask;

public class CityRecommender : TaskOrchestrator<int, object?>
{
    public override Task<object?> RunAsync(TaskOrchestrationContext context, int userChoice)
    {
        switch (userChoice)
        {
            case 1:
                context.SetCustomStatus(new
                {
                    recommendedCities = new[] { "Tokyo", "Seattle" },
                    recommendedSeasons = new[] { "Spring", "Summer" },
                });
                break;
            case 2:
                context.SetCustomStatus(new
                {
                    recommendedCities = new[] { "Seattle", "London" },
                    recommendedSeasons = new[] { "Summer" },
                });
                break;
            case 3:
                context.SetCustomStatus(new
                {
                    recommendedCities = new[] { "Tokyo", "London" },
                    recommendedSeasons = new[] { "Spring", "Summer" },
                });
                break;
        }

        // Wait for user selection and refine the recommendation
        return Task.FromResult<object?>(null);
    }
}

Fornire dati interattivi ai client

In questo modello, l'orchestratore espone informazioni sensibili al fattore tempo, ad esempio uno sconto, un URL di prenotazione e un timeout, tramite lo stato personalizzato, poi si interrompe e attende un evento esterno. Un client legge lo stato personalizzato per visualizzare l'offerta e invia l'evento di conferma all'orchestratore quando l'utente agisce.

[FunctionName("ReserveTicket")]
public static async Task<bool> Run(
  [OrchestrationTrigger] IDurableOrchestrationContext context)
{
  string userId = context.GetInput<string>();

  int discount = await context.CallActivityAsync<int>("CalculateDiscount", userId);

  context.SetCustomStatus(new
  {
    discount = discount,
    discountTimeout = 60,
    bookingUrl = "https://www.myawesomebookingweb.com",
  });

  bool isBookingConfirmed = await context.WaitForExternalEvent<bool>("BookingConfirmed");

  context.SetCustomStatus(isBookingConfirmed
    ? new {message = "Thank you for confirming your booking."}
    : new {message = "The booking was not confirmed on time. Please try again."});

  return isBookingConfirmed;
}
using System.Threading.Tasks;
using Microsoft.DurableTask;

public class ReserveTicket : TaskOrchestrator<string, bool>
{
    public override async Task<bool> RunAsync(TaskOrchestrationContext context, string userId)
    {
        int discount = await context.CallActivityAsync<int>("CalculateDiscount", userId);

        context.SetCustomStatus(new
        {
            discount,
            discountTimeout = 60,
            bookingUrl = "https://www.myawesomebookingweb.com",
        });

        bool isBookingConfirmed = await context.WaitForExternalEvent<bool>("BookingConfirmed");
        context.SetCustomStatus(isBookingConfirmed
            ? new { message = "Thank you for confirming your booking." }
            : new { message = "The booking was not confirmed on time. Please try again." });

        return isBookingConfirmed;
    }
}

Eseguire query sullo stato di orchestrazione personalizzato

Negli esempi precedenti viene illustrato come impostare uno stato personalizzato dal codice dell'orchestratore. Questa sezione è incentrata sul modo in cui i client esterni leggono tale valore.

Dopo che un agente di orchestrazione chiama SetCustomStatus, i client esterni possono eseguire query sul valore tramite l'API HTTP predefinita Durable Functions. Per esempio:

GET /runtime/webhooks/durabletask/instances/instance123

La risposta include il customStatus campo insieme ai metadati di runtime.

{
  "runtimeStatus": "Running",
  "input": null,
  "customStatus": { "nextActions": ["A", "B", "C"], "foo": 2 },
  "output": null,
  "createdTime": "2019-10-06T18:30:24Z",
  "lastUpdatedTime": "2019-10-06T19:40:30Z"
}

È anche possibile eseguire query sullo stato personalizzato a livello di codice usando l'SDK client di orchestrazione. Per informazioni di riferimento complete, vedere Istanze di query.

Gli SDK per attività durevoli non forniscono un endpoint di stato HTTP predefinito. Eseguire invece query sullo stato personalizzato a livello di codice usando le API dei metadati dell'istanza di orchestrazione in DurableTaskClient.

using Microsoft.DurableTask.Client;

OrchestrationMetadata? metadata = await client.GetInstanceAsync(instanceId, getInputsAndOutputs: true);
string? customStatusJson = metadata?.SerializedCustomStatus;

Avvertimento

Il payload di stato personalizzato è limitato a 16 KB di testo JSON UTF-16.

Passaggi successivi