【问题标题】:Correct use of Azure Durable Function - Serializing Complex Objects正确使用 Azure Durable Function - 序列化复杂对象
【发布时间】:2018-10-10 10:36:47
【问题描述】:

所以我正在制作一些 Azure 持久功能的原型,以尝试了解它们是否适合我们内部 API 系统的建议解决方案。

基于示例,我创建了一个Orchestrator 客户端 (HelloOrchestratorClient.cs),它响应HttpTrigger。该客户端从原始请求中提取一些信息,然后继续触发 Orchestrator Function (HelloOrchestrator.cs) 传递一些提取的信息:

复杂的 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 描述,你猜对了,一个标题),使用名为 HelloOrchestrator.APICallActivityTigger 到自己调用 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&lt;JsonProduct&gt;HelloOrchestrator.Run 返回时,我会收到在此 Gist(大堆栈跟踪)上找到的以下 NullReferenceException,并收到来自的 500 响应 Orchestrator 客户端

以下证明返回的output在运行时确实有一个对象:

可能是因为JsonProduct的复杂性(再次找到模型类here)?我问,因为当我将我的 Orchestrator Function 换成更简单的模型结构时,我没有收到 500,我收到我的 JSON有效载荷。

此示例显示Simple Orchestrator 函数HelloOrchestrator.cs,返回一个简单的TestToDo.cs (Gist for model) 平面对象,不会出错

简单的 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 之类的东西中使用以下内容(在 F5 之后):

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

当你运行它时,在 Postman 之类的东西中使用以下内容(在 F5 之后):

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

【问题讨论】:

    标签: c# azure azure-functions azure-durable-functions


    【解决方案1】:

    查看您发布的调用堆栈,NullReferenceException 似乎是 DurableOrchestrationClient 类中的错误。查看代码(您可以找到 here)似乎有可能,如果您使用的查询字符串无法正确解析,则可能是 null-ref。

    您提到您正在使用以下 URL 进行测试:

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

    我想知道最后两个字符 (&amp;N) 是否是问题的根源。是否可以对&amp; 进行编码或将其完全删除以隔离问题?

    无论如何,如果您能在此处记录问题,那就太好了:https://github.com/Azure/azure-functions-durable-extension/issues

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-01
    • 2014-01-01
    • 1970-01-01
    • 2020-01-07
    • 1970-01-01
    • 2011-05-09
    • 1970-01-01
    相关资源
    最近更新 更多