简体   繁体   English

如何执行Azure Queue触发的大量持久函数?

[英]How to execute a lot of durable functions triggered by Azure Queue?

If briefly, our task is to process a lot of input messages.简而言之,我们的任务是处理大量输入消息。

To solve this we decided to use Azure Queue Storage and Azure Functions.为了解决这个问题,我们决定使用 Azure 队列存储和 Azure Functions。 We have Azure Functions structure similar to the following code:我们有类似于以下代码的 Azure Functions 结构:

Queue triggered function队列触发功能

[FunctionName("MessageControllerExecutor")]
public static async void Run(
    [QueueTrigger(QUEUE_NAME, Connection = QUEUE_CONNECTION_NAME)]string queueMessage,
    [OrchestrationClient] DurableOrchestrationClient client,
    TraceWriter log)
{
    await client.StartNewAsync("MessageController", queueMessage);
}

Durable function耐用功能

[FunctionName("MessageController")]
public static async void Run(
    [OrchestrationTrigger] DurableOrchestrationContext context,
    TraceWriter log)
{
    if (!context.IsReplaying) log.Warning("MessageController started");

    var function1ResultTask = context.CallActivityAsync<ResultMessage>("Function_1", new InputMessage());
    var function2ResultTask = context.CallActivityAsync<ResultMessage>("Function_2", new InputMessage());

    await Task.WhenAll(function1ResultTask, function2ResultTask);

    // process Function_1 and Function_2 results
    // ...
}

Simple activity function sample简单活动函数示例

[FunctionName("Function_1")]
public static ResultMessage Run(
    [ActivityTrigger] DurableActivityContext activityContext,
    TraceWriter log)
{
    var msg = activityContext.GetInput<InputMessage>();
    int time = new Random().Next(1, 3);
    Thread.Sleep(time * 1000);

    return new ResultMessage()
    {
        Payload = $"Function_1 slept for {time} sec"
    };
}

MessageControllerExecutor triggered when a new item is received in a queue. MessageControllerExecutor在队列中接收到新项目时触发。 MessageController is a Durable Function that uses a few simple activity functions to process each message. MessageController是一个 Durable Function,它使用一些简单的活动函数来处理每条消息。

When we push messages to the queue, the MessageControllerExecutor function starts immediately and asynchronously fires the MessageController and passes the message, so this works as expected.当我们将消息推送到队列时, MessageControllerExecutor函数会立即启动并异步触发MessageController并传递消息,因此这按预期工作。

But we are faced with the problem.但是我们面临着这个问题。 Not all MessageController function instances run.并非所有MessageController函数实例都运行。

For example, we pushed 100 messages into the queue, but only about 10-20% of the messages were processed by MessageController .例如,我们将 100 条消息推送到队列中,但只有大约 10-20% 的消息被MessageController处理。

Some messages were not processed or were processed with a long delay.某些消息未处理或处理延迟很长时间。 It looks like durable functions failed to start б, though no exceptions were thrown.看起来持久函数未能启动 б,尽管没有抛出异常。

We have a few questions:我们有几个问题:

  1. Is this solution with queue triggered and durable functions correct to process the message queue or there is a better way to trigger durable functions by the queue?这个具有队列触发和持久函数的解决方案是否正确处理消息队列,或者有更好的方法来触发队列的持久函数?
  2. Are there any limitations to run durable functions?运行持久函数是否有任何限制?
  3. How many durable functions can be executed at the same time?可以同时执行多少个持久函数?
  1. Yes this is a totally valid way to kick off orchestrations!是的,这是开始编排的完全有效的方式!
  2. Sure, here's some details on the architecture as it relates to performance and scalability .当然, 这里有一些关于架构的细节,因为它与性能和可伸缩性有关
  3. I think what you're probably intending to ask here is: how many orchestration instances of a single durable function definition can be executed at the same time?您可能想在这里问的是:可以同时执行单个持久函数定义的多少个编排实例 This is indeed a very important aspect to understand.这确实是一个非常重要的方面需要理解。 Orchestration functions themselves are single threads and, per that link on scale I gave you above, are balanced across a set of control queues.编排功能本身是单线程,并且根据我上面提供的规模链接,在一组控制队列之间进行平衡。 You can read the document for more information, but the bottom line is you don't want to do any work other than actual orchestration in your orchestration function because they are your limit on scalability.您可以阅读该文档以获取更多信息,但最重要的是,除了在编排功能中进行实际编排之外,您不想做任何其他工作,因为它们是您对可扩展性的限制。 It is the orchestration action functions which behave like any other Azure Function and have virtually no limits on their scalability.它是业务流程操作函数,其行为与任何其他 Azure 函数类似,并且对其可扩展性几乎没有限制。

You did elide some code from your orchestration trigger in the question above for the purposes of brevity which I understand, but what exactly are you doing there after the await Task.WhenAll(...) ?为了我理解的简洁起见,您确实从上面的问题中的编排触发器中删除了一些代码,但是在await Task.WhenAll(...)之后您到底在做什么? If it includes any kind of significant processing you should really be farming that out to a third action function (eg Function_3 ) to do and then simply returning the results from the orchestration function.如果它包含任何类型的重要处理,您应该真正将其分配给第三个操作函数(例如Function_3 )来执行,然后简单地从编排函数返回结果。

Update: I just noticed your functions are defined as async void .更新:我刚刚注意到你的函数被定义为async void If I had to guess, this would actually cause a problem for the runtime.如果我不得不猜测,这实际上会导致运行时出现问题。 Can you try changing it to async Task and see if your problem goes away?您可以尝试将其更改为async Task并查看您的问题是否消失吗? As a general rule defining methods as async void is frowned upon in .NET .作为一般规则,在 .NET 中async void将方法定义为async void

Some extension for Drew's answer.德鲁答案的一些扩展。 You should not use Thread.Sleep(), as the documentation states , instead use CreateTimer Api.正如文档所述,您不应使用 Thread.Sleep(),而应使用 CreateTimer Api。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM