因此,我正在对一些Azure Durable Functions进行原型设计,以尝试了解它们是否适合我们内部 API 系统的建议解决方案。
基于示例,我创建了一个Orchestrator 客户端( HelloOrchestratorClient.cs
),它响应HttpTrigger
. 此客户端从原始请求中提取一些信息,然后继续触发Orchestrator 函数( 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 描述,你猜对了,一个标题),使用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(大堆栈跟踪)上找到的以下内容,并且我收到来自Orchestrator Client的500 响应。
下面证明output
返回的确实在运行时有一个对象:
可能是由于(再次在这里JsonProduct
找到模型类)的复杂性吗?我问,因为当我将我的Orchestrator 函数换成更简单的模型结构时,我没有收到 500,而是收到了我的 JSON 有效负载。
此示例显示了Simple Orchestrator 函数 HelloOrchestrator.cs
,返回一个简单的TestToDo.cs
(模型的要点)平面对象,该对象不会出错:
简单的 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 之后):
当你运行它时,在 Postman 之类的东西中使用以下内容(在 F5 之后):
http://localhost:7071/api/orchestrators/E1_Todo/wait?timeout=20&retryInterval=0.25