Azure Durable Functions에서 동적으로 오케스트레이터 선택

15837 단어 dotnetserverlessazure
이것은 함수에서 할 수 있다고 생각하는 멋진 일 중 하나이며, 또한 매우 흥미로운 메커니즘을 많이 구축할 수 있게 해줍니다. 실행하기가 그렇게 복잡하지는 않지만 이 기술을 사용한 결과는 매우 인상적인 결과로 이어질 수 있습니다. .

하지만 시작하기 전에 다음과 같은 면책 조항이 있습니다.

면책 조항 #1: 제가 준비한 코드 예제는 상당히 우스꽝스럽습니다. 하지만 비즈니스 로직에 초점을 맞춘 다른 아이디어가 있었기 때문에 이를 보여주는 데 이보다 더 간단한 코드를 찾을 수 없었습니다.

고지 사항 #2: 정말 우스꽝스럽습니다. 실생활에서 이와 같은 일을 하지 마십시오(내구성 함수 워크플로를 통한 문자열 구축 :D).

고지 사항 #3: 코드 예제에는 로깅 문이나 예외 처리가 많지 않습니다. 프로덕션 코드 남용 로깅 및 예외 처리 사용을 위해 #1과 같이 코드 예제는 이 기능을 이론적 개념으로 보여주도록 설계되었습니다. 가능한 한 명확하게 하십시오.

자, 이제 문제를 해결했으므로 파헤쳐 보겠습니다.

요구 사항:


  • 평소와 같이 내구성 기능 너깃이 설치된 기능 앱이 필요합니다(따라 하려면). 지침이 필요한 경우 이 항목link을 확인하십시오.
  • Azure 함수 및 Durable 함수에 대한 기본 이해

  • 우리는 무엇을 만들고 있습니까?



    이 예에서는 다음과 같은 시나리오가 있습니다.


    따라서 이름을 요청하는 기본 HTTP 트리거가 있고 이름을 반환하는 대신 이름을 "Dynamic Orchestrator"로 전달합니다. 이 오케스트레이터는 짧은 이름인지 긴 이름인지 결정하고 이를 기반으로 LongName 또는 ShortName 오케스트레이터를 호출합니다. 이러한 각 하위 오케스트레이터에는 인사말이 포함된 문자열을 실제로 빌드한 다음 반환하는 활동이 있습니다. 나는 훌륭한 예를 알고 있습니다! 어쨌든 이야기와 그림은 그만하고 코드로 들어가 봅시다.

    앞서 말했듯이 트리거는 기본적으로 몇 가지 작은 조정을 통해 템플릿에서 가져오는 트리거입니다.

    public static class DynamicOrchestratorTrigger
    {
        [FunctionName("DynamicOrchestratorTrigger")]
        public static async Task<IActionResult> RunAsync(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
            HttpRequest req, ILogger log,
            ExecutionContext executionContext,
            [DurableClient] IDurableOrchestrationClient starter)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
    
            string name = req.Query["name"];
    
            var requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name ??= data?.name;
            if (name == null)
            {
                return new BadRequestObjectResult("Please pass a name on the query string or in the request body");
            }
    
            var orchestratorInput = new OrchestratorInput
            {
                CorrelationId =  new Guid(),
                Name =  name.ToString()
            };
    
            var instanceId = await starter.StartNewAsync(nameof(DynamicOrchestrator), orchestratorInput);
    
            DurableOrchestrationStatus durableOrchestrationStatus = await starter.GetStatusAsync(instanceId);
            while (durableOrchestrationStatus.RuntimeStatus != OrchestrationRuntimeStatus.Completed)
            {
                await Task.Delay(200);
                durableOrchestrationStatus = await starter.GetStatusAsync(instanceId);
            }
    
            return (ActionResult) new OkObjectResult(durableOrchestrationStatus.Output);
        }
    }
    

    라인 30 – 35는 이 예제에 대한 해킹에 가깝습니다. 이것이 수행하는 작업은 기본적으로 완료될 때까지 오케스트레이터를 폴링하고 출력을 반환하는 것입니다. 이것이 수행되는 유일한 이유는 "귀하"가 쉽게 테스트하기 위함입니다. GitHub에서 프로젝트를 가져오면 이 정보를 얻을 수 있습니다(링크는 게시물 끝에 있습니다). 프로덕션에서 이와 같은 것을 사용하지 않는 것이 좋습니다.

    다음으로 DynamicOrchestrator를 살펴보겠습니다. 여기서 마법이 발생합니다.

    public class DynamicOrchestrator
    {
    
        [FunctionName(nameof(DynamicOrchestrator))]
        public async Task<string> Run([OrchestrationTrigger] IDurableOrchestrationContext context,
            ExecutionContext executionContext)
        {
            var input = context.GetInput<OrchestratorInput>();
    
            string greeting = "";
            switch (input.Name.Length)
            {
               case <= 4 :
                   greeting = await context.CallSubOrchestratorAsync<string>(nameof(ShortNameOrchestrator), input);
                   break;
               default:
                   greeting = await context.CallSubOrchestratorAsync<string>(nameof(LongNameOrchestrator), input);
                   break;
            }
    
            return greeting;
        }
    }
    

    그래서 이 시점에서 여러분은 "이건 정말 기가 막혔어..."또는 "굉장해!"처음에 말했듯이 별거 아닌 것처럼 보일 수도 있고 물론 이 예제도 도움이 되지 않지만 이 간단한 구조를 사용하면 정말 멋진 작업을 수행할 수 있습니다. 이것이 명확하지 않은 경우 이 코드를 다음과 같이 리팩터링하겠습니다.

    public class DynamicOrchestrator
    {
    
        [FunctionName(nameof(DynamicOrchestrator))]
        public async Task<string> Run([OrchestrationTrigger] IDurableOrchestrationContext context,
            ExecutionContext executionContext)
        {
            var input = context.GetInput<OrchestratorInput>();
            return await context.CallSubOrchestratorAsync<string>(
                await context.CallActivityAsync<string>(
                    nameof(DetermineOrchestratorToRunActivity), input.Name)
                ,input);
        }
    }
    

    기본적으로 여기서 일어난 일은 이 오케스트레이터를 HOF(고차 함수)에서 어느 정도 전환했습니다. 이렇게 하면 원할 경우 "DetermineOrchestratorToRunActivity"를 바꿀 수도 있습니다.

    "DetermineOrchestratorToRunActivity"에 대해 말하자면, 우리의 경우 이것은 다음 단계로 이어질 비즈니스 로직을 수행해야 하는 단순한 활동일 뿐입니다.

    public class DetermineOrchestratorToRunActivity
    {
    
        [FunctionName(nameof(DetermineOrchestratorToRunActivity))]
        public async Task<string> Run([ActivityTrigger] IDurableActivityContext  context)
        {
            var input= context.GetInput<string>();
            string nameOfOrchestrator;
    
            switch (input.Length)
            {
               case <= 4 :
                   return nameof(ShortNameOrchestrator);
                   break;
               default:
                   return nameof(LongNameOrchestrator);
                   break;
            }
        }
    }
    

    별거 아닌 것 같지만 여기에서 길이 스위치 대신 나중에 무엇이든 할 수 있습니다. 제 생각에는 이것이 꽤 흥미롭다고 생각합니다.

    나머지 오케스트레이터와 활동은 매우 멍청하고 단순하며 여기에 표시할 필요가 없다고 생각하지만 GitHubrepository에서 작업 예제를 볼 수 있습니다.

    좋은 웹페이지 즐겨찾기