Ange och fråga efter anpassad orkestreringsstatus

Med anpassad orkestreringsstatus kan du koppla godtyckliga JSON-metadata till en orkestreringsinstans som körs så att externa klienter kan köra frågor mot den när som helst. Använd anpassad status när du behöver:

  • Rapportförlopp mitt under flygning – låt ett användargränssnitt visa vilket steg en orkestrering har nått utan att vänta på att den ska slutföras.
  • Returnera dynamiska data till anropare – visa rekommendationer, rabattinformation eller instruktioner för nästa steg medan orkestreringen fortfarande körs.
  • Samordna med externa system – dela tillstånd som andra tjänster eller mänskliga operatörer kan avsöka och agera på.

Varning

Nyttolasten för anpassad status är begränsad till 16 KB UTF-16 JSON-text. Om du behöver en större nyttolast använder du extern lagring och lagrar en referens (till exempel en blob-URL) i den anpassade statusen i stället.

I Azure Functions är den här statusen tillgänglig via HTTP GetStatus API eller motsvarande SDK API på orkestreringsklientobjektet.

I Durable Task SDK:er kan du få tillgång till denna status via SDK:ns orkestreringsmetadata-API:er (till exempel GetInstanceAsync i .NET, getInstanceMetadata i Java eller FetchOrchestrationMetadata i Go). Go-orkestrerare anger typad anpassad status med ctx.SetCustomStatusValue och kontrollera dess fel. Go continue-as-new-exemplet rapporterar sitt antal cykler på detta sätt.

Viktigt!

PowerShell Durable Task SDK är för närvarande inte tillgängligt.

Exempel på användningsfall för anpassad orkestreringsstatus

I följande tabell sammanfattas vanliga mönster. Välj ett användningsfall för att gå vidare till motsvarande exempel.

Användningsfall Beskrivning
Visualisera orkestreringsframstatus Uppdatera en sträng eller ett objekt efter varje aktivitet så att klienter kan visa en förloppsindikator.
Returnera dynamiska metadata till klienter Ange strukturerade data (till exempel rekommendationer) som klienter renderar utan att behöva anpassade slutpunkter på serversidan.
Tillhandahålla åtgärdsbara data till klienter Visa URL:er för bokning, rabattinformation eller instruktioner för nästa steg som klienterna kan agera på medan orkestreringen väntar på en extern händelse.
Fråga efter anpassad status Läs det anpassade statusvärdet från en klient med http-API:er eller SDK-anrop.

Visualisera orkestreringsframstatus

I det här mönstret anropar orkestratorn SetCustomStatus (eller motsvarande på ditt språk) efter att varje aktivitet har slutförts och uppdaterar statusen med namnet på den senast slutförda staden. En klient avsöker statusslutpunkten, läser det aktuella värdet och uppdaterar en förloppsindikator i användargränssnittet.

Följande exempel visar förloppsdelning med hjälp av Durable Functions HTTP-statusslutpunkt:

Anmärkning

De här exemplen är skrivna för Durable Functions 2.x och är inte kompatibla med Durable Functions 1.x. Mer information om skillnaderna mellan versioner finns i artikeln Durable Functions-versioner .


Isolerad arbetsmodell
[Function("E1_HelloSequence")]
public static async Task<List<string>> Run(
    [OrchestrationTrigger] TaskOrchestrationContext 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;
}

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

Processmodell
[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}!";
}

Följande exempel visar förloppsdelning med SDK-klient-API:er för Durable Task:

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

Klienten kan avsöka orkestreringsmetadata och vänta tills fältet CustomStatus är inställt på "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;
}

Följande klientkod avsöker statusen för orkestreringen och väntar tills CustomStatus har angetts till "London" innan ett svar returneras.


Isolerad arbetsmodell
[Function("HttpStart")]
public static async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "orchestrators/{functionName}")] HttpRequestData req,
    [DurableClient] DurableTaskClient client,
    string functionName,
    FunctionContext executionContext)
{
    ILogger log = executionContext.GetLogger("HttpStart");
    string? eventData = await req.ReadFromJsonAsync<string>();
    string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(functionName, eventData);

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

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

    HttpResponseData response = req.CreateResponse(HttpStatusCode.OK);
    await response.WriteAsJsonAsync(metadata);
    return response;
}

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

Returnera dynamiska metadata till klienter

Du kan använda anpassad orkestreringsstatus för att returnera strukturerade data , till exempel anpassade rekommendationer, till klienter utan att skapa separata slutpunkter. Orchestrator anger den anpassade statusen baserat på indata och klienten läser den via standardstatus-API:et. Detta behåller kod på klientsidan generisk medan all logik finns kvar på serversidan.


Isolerad arbetsmodell
[Function("CityRecommender")]
public static void Run([OrchestrationTrigger] TaskOrchestrationContext 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
}

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

Tillhandahålla åtgärdsbara data till klienter

I det här mönstret visar orkestreraren tidskänslig information – till exempel rabatt, en boknings-URL och en timeout – via anpassad status och pausar sedan för att vänta på en extern händelse. En klient läser den anpassade statusen för att visa erbjudandet och skickar bekräftelsehändelsen tillbaka till orchestrator när användaren agerar.


Isolerad arbetsmodell
[Function("ReserveTicket")]
public static async Task<bool> Run(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    string userId = context.GetInput<string>();

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

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

Fråga efter anpassad orkestreringsstatus

I föregående exempel visas hur du anger anpassad status från orchestrator-kod. Det här avsnittet fokuserar på hur externa klienter läser det värdet.

När en orchestrator anropar SetCustomStatus kan externa klienter fråga efter värdet via det inbyggda Durable Functions HTTP-API:et. Som exempel:

GET /runtime/webhooks/durabletask/instances/instance123

Svaret innehåller fältet customStatus tillsammans med körningsmetadata:

{
  "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"
}

Du kan också anropa anpassad status programmatiskt med hjälp av orkestreringsklientens SDK. En fullständig referens finns i Frågeinstanser.

Durable Task SDK:er tillhandahåller inte någon inbyggd HTTP-statusslutpunkt. Istället kan du fråga anpassad status programmatiskt med hjälp av SDK:ns API:er för orkestreringsinstansmetadata.

using Microsoft.DurableTask.Client;

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

Varning

Nyttolasten för anpassad status är begränsad till 16 KB UTF-16 JSON-text.

Nästa steg