사용자 지정 오케스트레이션 상태 설정 및 쿼리

사용자 지정 오케스트레이션 상태를 사용하면 외부 클라이언트가 언제든지 쿼리할 수 있도록 임의의 JSON 메타데이터를 실행 중인 오케스트레이션 인스턴스에 연결할 수 있습니다. 다음을 수행해야 하는 경우 사용자 지정 상태를 사용합니다.

  • 진행 중 상황 보고 - 오케스트레이션이 완료되기를 기다리지 않고 UI에서 오케스트레이션의 현재 단계를 표시하도록 합니다.
  • 오케스트레이션이 계속 실행되는 동안 호출자에게 동적 데이터를 반환합니다( 화면 권장 사항, 할인 정보 또는 다음 단계 지침).
  • 외부 시스템과의 조정 – 다른 서비스나 인간 운영자가 폴링하고 작업할 수 있는 상태를 공유합니다.

경고

사용자 지정 상태 페이로드는 16KB의 UTF-16 JSON 텍스트로 제한됩니다. 더 큰 페이로드가 필요한 경우 외부 스토리지를 사용하고 대신 사용자 지정 상태에 참조(예: Blob URL)를 저장합니다.

Azure Functions 이 상태는 오케스트레이션 클라이언트 개체의 HTTP GetStatus API 또는 해당 SDK API 통해 사용할 수 있습니다.

지속성 작업 SDK에서 이 상태는 DurableTaskClient 오케스트레이션 상태 쿼리 API를 통해 사용할 수 있습니다(예: .NET GetInstanceAsync 또는 Java getInstanceMetadata).

중요합니다

현재 PowerShell 지속성 작업 SDK는 사용할 수 없습니다.

사용자 지정 오케스트레이션 상태에 대한 샘플 사용 사례

다음 표에서는 일반적인 패턴을 요약합니다. 사용 사례를 선택하여 해당 예제로 이동합니다.

사용 사례 설명
오케스트레이션 진행률 시각화 클라이언트가 진행률 표시기를 표시할 수 있도록 각 작업 후에 문자열 또는 개체를 업데이트합니다.
클라이언트에 동적 메타데이터 반환 사용자 지정 서버 쪽 엔드포인트 없이 클라이언트가 렌더링하는 구조적 데이터(예: 권장 사항)를 설정합니다.
클라이언트에 실행 가능한 데이터 제공 오케스트레이션이 외부 이벤트를 기다리는 동안, 클라이언트가 수행할 예약 URL, 할인 정보 또는 다음 단계 지침을 표시합니다.
사용자 지정 상태 쿼리 HTTP API 또는 SDK 호출을 사용하여 클라이언트에서 사용자 지정 상태 값을 읽습니다.

오케스트레이션 진행률 시각화

이 패턴에서 오케스트레이터는 각 활동이 완료된 후(또는 해당 언어로) 호출 SetCustomStatus 하여 마지막으로 완료된 도시의 이름으로 상태를 업데이트합니다. 클라이언트는 상태 엔드포인트를 폴링하고, 현재 값을 읽고, UI에서 진행률 표시기를 업데이트합니다.

다음 샘플에서는 Durable Functions HTTP 상태 엔드포인트를 사용하여 진행률 공유를 보여 줍니다.

메모

이러한 예제는 Durable Functions 2.x용으로 작성되었으며 Durable Functions 1.x와 호환되지 않습니다. 버전 간의 차이점에 대한 자세한 내용은 Durable Functions 버전 문서를 참조하세요.

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

다음 샘플에서는 지속성 작업 SDK 클라이언트 API를 사용하여 진행률 공유를 보여 줍니다.

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

클라이언트는 오케스트레이션 메타데이터를 폴링하고 CustomStatus 필드가 "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;
}

다음 클라이언트 코드는 오케스트레이션 상태를 폴링하고, CustomStatus"London"으로 설정될 때까지 기다린 후 응답을 반환합니다.

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

클라이언트에 동적 메타데이터 반환

사용자 지정 오케스트레이션 상태를 사용하여 개별 엔드포인트를 빌드하지 않고도 개인 설정된 권장 사항과 같은 구조적 데이터를 클라이언트에 반환할 수 있습니다. 오케스트레이터는 입력에 따라 사용자 지정 상태를 설정하고 클라이언트는 표준 상태 API를 통해 읽습니다. 이렇게 하면 모든 논리가 서버 쪽에 유지되는 동안 클라이언트 쪽 코드가 제네릭으로 유지됩니다.

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

클라이언트에 실행 가능한 데이터 제공

이 패턴에서 오케스트레이터는 사용자 지정 상태를 통해 할인, 예약 URL 및 시간 제한과 같은 시간에 민감한 정보를 표시한 다음, 일시 중지하여 외부 이벤트를 기다립니다. 클라이언트는 사용자 지정 상태를 읽고 제품을 표시하고 사용자가 작업을 할 때 확인 이벤트를 다시 오케스트레이터로 보냅니다.

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

사용자 지정 오케스트레이션 상태 쿼리

이전 예제에서는 오케스트레이터 코드에서 사용자 지정 상태를 설정하는 방법을 보여 줍니다. 이 섹션에서는 외부 클라이언트가 해당 값을 읽는 방법에 중점을 둡니다.

오케스트레이터가 SetCustomStatus 호출한 후 외부 클라이언트는 기본 제공 Durable Functions HTTP API를 통해 값을 쿼리할 수 있습니다. 다음은 그 예입니다.

GET /runtime/webhooks/durabletask/instances/instance123

응답에는 런타임 메타데이터와 함께 필드가 포함됩니다 customStatus .

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

오케스트레이션 클라이언트 SDK를 사용하여 프로그래밍 방식으로 사용자 지정 상태를 쿼리할 수도 있습니다. 전체 참조는 쿼리 인스턴스를 참조하세요.

지속성 작업 SDK는 기본 제공 HTTP 상태 엔드포인트를 제공하지 않습니다. 대신 오케스트레이션 인스턴스 메타데이터 API를 사용하여 DurableTaskClient에서 프로그래밍 방식으로 사용자 지정 상태를 쿼리합니다.

using Microsoft.DurableTask.Client;

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

경고

사용자 지정 상태 페이로드는 16KB의 UTF-16 JSON 텍스트로 제한됩니다.

다음 단계