我在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并记录响应 . 凉 .
当“产生队列消息的东西”出现大幅增加并且许多项目立即被放入队列时,就会出现问题 . 这往往会在一分钟内发生大约1,000-1,500件物品 . 错误日志将具有以下内容:
2017-02-14T01:45:31.692 mscorlib:执行函数时出现异常:Functions.SendToLimeade . f-SendToLimeade __- 1078179529:发送请求时发生错误 . 系统:无法连接到远程服务器 . 系统:通常只允许使用每个套接字地址(协议/网络地址/端口)123.123.123.123:443 .
起初,我认为这是Azure功能应用程序运行本地套接字的问题,如illustrated here . 但是,我注意到了IP地址 . IP地址123.123.123.123(当然在本例中已更改)是我们的IP地址,即HttpClient发布的IP地址 . 所以,现在我想知道是不是我们的服务器用完了套接字来处理这些请求 .
无论哪种方式,我们都会遇到扩展问题 . 我正试图找出解决问题的最佳方法 .
Some ideas:
-
如果是本地套接字限制,article above有一个使用
Req.ServicePoint.BindIPEndPointDelegate
增加本地端口范围的示例 . 这似乎很有希望,但是当你真正需要扩展时,你会怎么做?我不希望这个问题在2年内回归 . -
如果's a remote limitation, it looks like I can control how many messages the Functions runtime will process at once. There'这是一篇有趣的文章,说你可以将
serviceBus.maxConcurrentCalls
设置为1,一次只能处理一条消息 . 也许我可以将它设置为相对较低的数字 . 现在,在某些时候,我们的队列填充速度将超过我们处理它们的速度,但此时答案是在我们的末端添加更多服务器 . -
多个Azure功能应用?如果我有多个Azure Functions应用程序并且它们都在同一队列上触发,会发生什么? Azure是否足够智能,可以在功能应用程序之间分配工作,我可以让大量机器处理我的队列,可以根据需要按比例放大或缩小?
-
我've also come across keep-alives. It seems to me if I could somehow keep my socket open as queue messages were flooding in, it could perhaps help greatly. Is this possible, and any tips on how I' d去做这个?
对于这种系统的推荐(可扩展!)设计的任何见解将不胜感激!
4 回答
我想我已经在过去3小时6小时内运行了这些更改,并且我的零插槽错误 . 在我每30分钟左右大批量地收到这些错误之前 .
首先,我添加了一个新类来管理HttpClient .
现在,我们有一个静态实例
HttpClient
,我们用它来调用函数 . 根据我的研究,强烈建议尽可能长时间地保留HttpClient实例,一切都是线程安全的,HttpClient会将请求排队并优化对同一主机的请求 . 注意我也设置了Keep-Alive
Headers (我认为这是默认的,但我认为我会隐含) .在我的函数中,我只是 grab 静态HttpClient实例,如:
我还没有真正深入分析套接字级别发生的事情(我必须问问我们的IT人员是否能够在负载均衡器上看到这种流量),但我希望它只是保持单个套接字对我们的服务器开放,并在处理队列项时进行一堆HTTP调用 . 无论如何,无论它做什么似乎都在起作用 . 也许有人对如何改进有一些想法 .
我认为代码错误是因为:
using (var client = new HttpClient())
引自Improper instantiation antipattern:
如果您在专用Web应用程序上使用消费计划而不是功能,则开箱即用的#3或多或少 . 函数将检测到您有一个大的消息队列,并将添加实例,直到队列长度稳定 .
maxConcurrentCalls
仅适用于每个实例,允许您限制每个实例的并发性 . 基本上,您的处理率是maxConcurrentCalls * instanceCount
.控制全局吞吐量的唯一方法是在您选择的大小的专用Web应用程序上使用函数 . 每个应用程序将轮询队列并根据需要抓取工作 .
最佳扩展解决方案将改进123.123.123.123上的负载 balancer ,以便它可以处理来自函数扩展/缩小以满足队列压力的任意数量的请求 .
保持活动afaik对于持久连接很有用,但是函数执行不会被视为持久连接 . 将来我们会尝试为函数添加“自带绑定”,如果你愿意,可以实现连接池 .
我知道很久以前就回答了这个问题,但与此同时,微软已经记录了你的反模式使用 .
Improper Instantiation antipattern