Правильное использование надежной функции Azure - сериализация сложных объектов - PullRequest
0 голосов
/ 10 октября 2018

Итак, я создаю прототип некоторых Azure Durable Functions , чтобы попытаться понять, подойдут ли они к предлагаемому решению для нашей внутренней системы API.

Основываясь на примерах, ясоздал клиент Orchestrator (HelloOrchestratorClient.cs), который отвечает на HttpTrigger.Этот клиент извлекает некоторую информацию из исходного запроса, затем переходит к запуску функции Orchestrator (HelloOrchestrator.cs), передавая часть извлеченной информации:

Complex HelloOrchestratorClient.cs:

[FunctionName("HttpSyncStart")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, methods: "get", Route = "orchestrators/{functionName}/wait")]
    HttpRequestMessage req,
    [OrchestrationClient] DurableOrchestrationClient starter,
    string functionName,
    ILogger log)
{       
    HttpReq originalRequest = new HttpReq() {
            DeveloperId = GetDevKey(req,apiHeaderKey),
            QueryString = req.RequestUri.Query,
            APIName = GetQueryStringValue(req,APIName),
            APIVersion = GetQueryStringValue(req,APIVersion)

    };
    string instanceId =   await starter.StartNewAsync(functionName, originalRequest);

    TimeSpan timeout = GetTimeSpan(req, Timeout) ?? TimeSpan.FromSeconds(30);
    TimeSpan retryInterval = GetTimeSpan(req, RetryInterval) ?? TimeSpan.FromSeconds(1);

    return  await starter.WaitForCompletionOrCreateCheckStatusResponseAsync(
        req,
        instanceId,
        timeout,
        retryInterval);

}

На данный момент HelloOrchestrator.cs просто вызывает один из наших внутренних API и возвращает полезную нагрузку JsonProduct (Простое POCO, описывающее, как вы уже догадались, заголовок), используя ActivityTigger с именемHelloOrchestrator.APICall для вызова самого API.

Сложный HelloOrchestrator.cs:

  [FunctionName("E1_JsonProduct")]
        public static async Task<List<JsonProduct>> Run(
            [OrchestrationTrigger] DurableOrchestrationContextBase context,
            ILogger log)
        {
            List<JsonProduct> output = new List<JsonProduct>();
            HttpReq r = context.GetInput<HttpReq>();
            if(r != null)
            {
                if(r.DeveloperId == null)
                {
                    return output;
                }
                output.Add(await context.CallActivityAsync<JsonProduct>("E1_CallAPI",r));
                return output;
            }
            return output;
        } 

[FunctionName("E1_CallAPI")]
public async static Task<JsonProduct> APICall([ActivityTrigger] HttpReq req,
    ILogger log)
{

    JsonProduct products  = null;
    string u = $"{baseAddress}{req.APIVersion}/{req.APIName}{req.QueryString}";  

    var request = new HttpRequestMessage(HttpMethod.Get, u);
    request.Headers.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json")
    );
    request.Headers.Add("x-apikey",req.DeveloperId);
     log.LogInformation($"URL calling = '{request.RequestUri.AbsoluteUri}'.");
    HttpResponseMessage response = await client.SendAsync(request);
    // return await response.Content.ReadAsStringAsync();
    if(response.IsSuccessStatusCode)
    {
        var formatter = new JsonMediaTypeFormatter
        {
            SerializerSettings = HelloProj.CosmosDB.Models.Products.Converter.Settings
        };

        products = await response.Content.ReadAsAsync<JsonProduct>(new [] {formatter});
    }
    return products;
}

Стороннее примечание: План, если я могу заставить это работать, состоит в том, чтобы разветвлять кучу процессов к различнымAPI и веер снова включаются, объединяют полезную нагрузку JSON и возвращают ее отправителю.

Проблема, с которой я сталкиваюсь

Итак, когда мой List<JsonProduct> возвращается из HelloOrchestrator.RunЯ получаю следующее NullReferenceException, найденное в этом Gist (Трассировка большого стека), и я получаю 500 ответ от Orchestrator Client .

Следующее доказывает, что возвращенный output действительно имеет объект во время выполнения:

Runtime Screen Grab of output

Может ли это быть из-за сложности JsonProduct(Снова найти модель классов здесь )?Я спрашиваю, потому что, когда я заменяю свою функцию оркестровщика на более простую структуру модели, я не получаю 500, я получаю свою JSON Payload.

В этом примере показана функция Simple Orchestrator HelloOrchestrator.cs, возвращающая простой TestToDo.cs ( Gist для модели ) плоский объект, который не содержит ошибок :

Простой HelloOrchestrator.cs:

   [FunctionName("E1_Todo")]
    public static async Task<TestToDo> RunToDo(
    [OrchestrationTrigger] DurableOrchestrationContextBase context,
        ILogger log)
    {
        HttpReq r = context.GetInput<HttpReq>();
        TestToDo todo = new TestToDo();
        if(r != null)
        {
            todo = await context.CallActivityAsync<TestToDo>("E1_CallAPITodo",r);
        }
        return todo;
    }

[FunctionName("E1_CallAPITodo")]
public async static Task<TestToDo> APITodoCall([ActivityTrigger] HttpReq req,
    ILogger log)
{

    var request = new HttpRequestMessage(HttpMethod.Get, "https://jsonplaceholder.typicode.com/todos/1");
    request.Headers.Accept.Add(
        new MediaTypeWithQualityHeaderValue("application/json")
    );
     log.LogInformation($"URL calling = '{request.RequestUri.AbsoluteUri}'. for {req.QueryString}");
    HttpResponseMessage response = await client.SendAsync(request);
    return await response.Content.ReadAsAsync<TestToDo>();
} 

Дополнительная информация

Если вам нужны мои полные прототипы проектов, вы можете найти их здесь:

Когда вы его запустите, используйте что-то вроде Postman (после F5ing):

http://localhost:7071/api/orchestrators/E1_JsonProduct/wait?timeout=20&retryInterval=0.25&api=products&apiVersion=v1&filterByImprints=W%26N&N

При запуске используйтепосле чего-то вроде Почтальона (после F5ing):

http://localhost:7071/api/orchestrators/E1_Todo/wait?timeout=20&retryInterval=0.25

1 Ответ

0 голосов
/ 11 октября 2018

Если посмотреть на опубликованный вами стек вызовов, то NullReferenceException представляется ошибкой в ​​классе DurableOrchestrationClient.Глядя на код (который вы можете найти здесь ), кажется возможным, что если строка запроса, которую вы используете, не может быть правильно проанализирована, возможен нуль-ref.

Вы упомянули васдля тестирования используется следующий URL:

http://localhost:7071/api/orchestrators/E1_JsonProduct/wait?timeout=20&retryInterval=0.25&api=products&apiVersion=v1&filterByImprints=W%26N&N

Интересно, являются ли последние два символа (&N) источником проблемы.Можно ли закодировать & или удалить его полностью, чтобы изолировать проблему?

В любом случае, было бы здорово, если бы вы могли зарегистрировать проблему здесь: https://github.com/Azure/azure-functions-durable-extension/issues

...