限制Azure Functions队列

时间:2017-02-14 19:16:37

标签: azure azure-functions azure-queues

我在Azure中有一个Function项目,当项目放入队列时会触发该应用程序。它看起来像这样(大大简化):

public static async Task Run(string myQueueItem, TraceWriter log)
{
    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(Config.APIUri);
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        StringContent httpContent = new StringContent(myQueueItem, Encoding.UTF8, "application/json");
        HttpResponseMessage response = await client.PostAsync("/api/devices/data", httpContent);
        response.EnsureSuccessStatusCode();

        string json = await response.Content.ReadAsStringAsync();
        ApiResponse apiResponse = JsonConvert.DeserializeObject<ApiResponse>(json);

        log.Info($"Activity data successfully sent to platform in {apiResponse.elapsed}ms.  Tracking number: {apiResponse.tracking}");
    }
}

这一切都很好,运行得很好。每次将项目放入队列时,我们都会将数据发送到我们这边的某个API并记录响应。凉。

问题发生在&#34;产生队列消息&#34;并且很多项目立即被放入队列中。这往往会在一分钟内发生大约1,000到1,500件物品。错误日志将具有以下内容:

  

2017-02-14T01:45:31.692 mscorlib:执行函数时出现异常:   Functions.SendToLimeade。 f-SendToLimeade __- 1078179529:错误   发送请求时发生。系统:无法连接到   远程服务器。系统:每个套接字地址只有一种用法   (协议/网络地址/端口)通常是允许的   123.123.123.123:443。

起初,我认为这是Azure Function应用程序运行本地套接字的问题,如illustrated here。但是,我注意到了IP地址。 IP地址123.123.123.123(当然在本例中已更改)是我们的IP地址,即HttpClient发布的IP地址。所以,现在我想知道是否我们的服务器用完了套接字来处理这些请求。

无论哪种方式,我们都会遇到扩展问题。我试图找出解决问题的最佳方法。

一些想法:

  1. 如果它是本地套接字限制,article above有一个使用Req.ServicePoint.BindIPEndPointDelegate增加本地端口范围的示例。这似乎很有希望,但当你真正需要扩展时,你会怎么做?我不希望这个问题在2年内回归。
  2. 如果它是一个远程限制,看起来我可以控制函数运行时一次处理多少消息。这里有一篇有趣的文章说你可以将serviceBus.maxConcurrentCalls设置为1,并且一次只能处理一条消息。也许我可以将其设置为相对较低的数字。现在,在某些时候,我们的队列填充速度将超过我们处理它们的速度,但此时答案是在我们的末端添加更多服务器。
  3. 多个Azure功能应用?如果我有多个Azure Functions应用程序并且它们都在同一队列上触发,会发生什么? Azure是否足够聪明,可以在功能应用程序之间分配工作,我可以让大量机器处理我的队列,可以根据需要按比例放大或缩小?
  4. 我也遇到了保持活力。在我看来,如果我可以以某种方式保持我的套接字打开,因为队列消息泛滥,它可能会有很大帮助。这可能吗,以及关于我如何做到这一点的任何提示?
  5. 对于此类系统的推荐(可扩展!)设计的任何见解将不胜感激!

4 个答案:

答案 0 :(得分:7)

我想我已经找到了解决方案。我已经在过去的 3小时 6小时内运行这些更改,并且我没有套接字错误。在我每30分钟左右大批量地收到这些错误之前。

首先,我添加了一个新类来管理HttpClient。

public static class Connection
{
    public static HttpClient Client { get; private set; }

    static Connection()
    {
        Client = new HttpClient();

        Client.BaseAddress = new Uri(Config.APIUri);
        Client.DefaultRequestHeaders.Add("Connection", "Keep-Alive");
        Client.DefaultRequestHeaders.Add("Keep-Alive", "timeout=600");
        Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    }
}

现在,我们有一个HttpClient的静态实例,我们用于每次调用该函数。根据我的研究,强烈建议尽可能长时间地保持HttpClient实例,一切都是线程安全的,HttpClient会将请求排队并优化对同一主机的请求。请注意,我还设置了Keep-Alive标题(我认为这是默认标题,但我认为我是隐含的)。

在我的函数中,我只是抓住静态HttpClient实例,如:

var client = Connection.Client;
StringContent httpContent = new StringContent(myQueueItem, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync("/api/devices/data", httpContent);
response.EnsureSuccessStatusCode();

我还没有真正深入分析套接字层面发生的事情(我不得不问问我们的IT人员他们是否能够看到此流量在负载均衡器上),但我希望它只是保持一个单独的套接字打开我们的服务器,并在处理队列项时进行一堆HTTP调用。无论如何,无论它做什么似乎都在起作用。也许有人对如何改进有一些想法。

答案 1 :(得分:4)

I think the code error is because of: using (var client = new HttpClient())

Quoted from Improper instantiation antipattern:

this technique is not scalable. A new HttpClient object is created for each user request. Under heavy load, the web server may exhaust the number of available sockets.

答案 2 :(得分:1)

如果您在专用网络应用程序上使用消费计划而不是功能,则开箱即用的#3或多或少。函数将检测到您有一个大的消息队列,并将添加实例,直到队列长度稳定。

maxConcurrentCalls仅适用于每个实例,允许您限制每个实例的并发性。基本上,您的处理率为maxConcurrentCalls * instanceCount

控制全局吞吐量的唯一方法是在您选择的大小的专用Web应用程序上使用函数。每个应用程序都会轮询队列并根据需要抓取工作。

最佳扩展解决方案将改善123.123.123.123上的负载平衡,以便它可以处理来自函数扩展/缩小以满足队列压力的任意数量的请求。

保持活动afaik对于持久连接很有用,但是函数执行不被视为持久连接。在未来,我们正在尝试添加“自带绑定”#39; to functions,如果你愿意,可以实现连接池。

答案 3 :(得分:1)

我知道很久以前就回答了这个问题,但与此同时,微软已经记录了你正在使用的反模式。

Improper Instantiation antipattern