限制 Azure Functions 队列上的并发作业数
Posted
技术标签:
【中文标题】限制 Azure Functions 队列上的并发作业数【英文标题】:Limiting the number of concurrent jobs on Azure Functions queue 【发布时间】:2017-07-03 05:31:54 【问题描述】:我在 Azure 中有一个函数应用程序,它在将项目放入队列时触发。它看起来像这样(非常简化):
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.elapsedms. Tracking number: apiResponse.tracking");
这一切都很好,运行得很好。每次将项目放入队列时,我们都会将数据发送到我们这边的某个 API 并记录响应。很酷。
当“生成队列消息的事物”出现大幅峰值并且同时将大量项目放入队列时,就会出现问题。这往往在一分钟内发生大约 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 发布到的那个。所以,现在我想知道是不是 我们的 服务器用完了套接字来处理这些请求。
无论哪种方式,我们都会遇到扩展问题。我正在尝试找出解决它的最佳方法。
一些想法:
-
如果是本地套接字限制,article above 有一个使用
Req.ServicePoint.BindIPEndPointDelegate
增加本地端口范围的示例。这看起来很有希望,但是当你真正需要扩展时你会怎么做?我不希望这个问题在 2 年后再次出现。
如果是远程限制,看起来我可以控制 Functions 运行时一次处理多少消息。这里有一篇有趣的文章说您可以将serviceBus.maxConcurrentCalls
设置为1,并且一次只会处理一条消息。也许我可以将其设置为相对较低的数字。现在,在某个时候,我们的队列会比我们处理它们的速度更快,但此时的答案是在我们端添加更多服务器。
多个 Azure Functions 应用程序?如果我有多个 Azure Functions 应用并且它们都在同一个队列上触发,会发生什么情况? Azure 是否足够聪明,可以在 Function 应用程序之间分配工作,并且我可以让一大群机器处理我的队列,可以根据需要扩大或缩小?
我也遇到过keep-alives。在我看来,如果我能在队列消息涌入时以某种方式保持我的套接字打开,它可能会有很大帮助。这可能吗,以及我将如何去做的任何提示?
对于此类系统的推荐(可扩展!)设计的任何见解将不胜感激!
【问题讨论】:
【参考方案1】:我认为代码错误是因为:using (var client = new HttpClient())
引用自Improper instantiation antipattern:
此技术不可扩展。创建一个新的 HttpClient 对象 每个用户请求。在重负载下,Web 服务器可能会耗尽 可用套接字的数量。
【讨论】:
【参考方案2】:我知道这个问题很久以前就得到了回答,但与此同时,Microsoft 已经记录了您使用的反模式。
Improper Instantiation antipattern
【讨论】:
【参考方案3】:我想我已经找到了解决方案。在过去的 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 调用。无论如何,无论它在做什么似乎都在起作用。也许有人对如何改进有一些想法。
【讨论】:
【参考方案4】:如果您在专用 Web 应用程序上使用消费计划而不是 Functions,则第 3 点或多或少会开箱即用。函数会检测到你有一个很大的消息队列,并会添加实例直到队列长度稳定。
maxConcurrentCalls
仅适用于每个实例,允许您限制每个实例的并发性。基本上,你的处理率是maxConcurrentCalls * instanceCount
。
控制全局吞吐量的唯一方法是在您选择的大小的专用网络应用程序上使用函数。每个应用都会轮询队列并根据需要获取工作。
最好的扩展解决方案将改善 123.123.123.123 上的负载平衡,以便它可以处理来自函数扩展/缩减的任意数量的请求,以满足队列压力。
Keep alive afaik 对于持久连接很有用,但函数执行不被视为持久连接。将来我们会尝试将“自带绑定”添加到 Functions,如果您愿意,这将允许您实现连接池。
【讨论】:
现在,它看起来已经在使用四个不同的主机(根据 New Relic)来处理缩放。所以,我想关于添加新功能应用程序的部分不是必需的;它已经这样做了。我仍然不清楚套接字错误消息是Azure上的本地实例没有套接字还是我们这里的服务器没有套接字。 也在下面添加了我自己的答案;到目前为止,这似乎对我们有用! 这个类似的问题/答案可能也很有趣:***.com/questions/40094041/… @MikeChristensen 您是否有指向“自带绑定”功能之后的 github 里程碑或问题的链接,以便我跟踪其进度?以上是关于限制 Azure Functions 队列上的并发作业数的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Python Azure Functions QueueTrigger 手动使消息出队?
Azure Functions 的 Azure 队列触发器:配置最小轮询间隔
Azure Functions:我可以对 BlobTriggered 函数进行不同的配置吗?
使用Azure Functions 在web 应用中启用自动更新分析基于轮询的 Web 应用的限制