Mik*_*sen 3 azure azure-queues azure-functions
我在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}");
}
}
Run Code Online (Sandbox Code Playgroud)
这一切都很好,运行得很好.每次将项目放入队列时,我们都会将数据发送到我们这边的某个API并记录响应.凉.
当"产生队列消息的东西"出现大幅增加并且许多项目立即被放入队列时,就会出现问题.这往往会在一分钟内发生大约1,000到1,500件物品.错误日志将具有以下内容:
2017-02-14T01:45:31.692 mscorlib:执行函数时出现异常:Functions.SendToLimeade.f-SendToLimeade __- 1078179529:发送请求时发生错误.系统:无法连接到远程服务器.系统:通常只允许使用每个套接字地址(协议/网络地址/端口)123.123.123.123:443.
起初,我认为这是Azure功能应用程序运行本地套接字的问题,如此处所示.但是,我注意到了IP地址.IP地址123.123.123.123(当然在本例中已更改)是我们的IP地址,即HttpClient发布的IP地址.所以,现在我想知道是不是我们的服务器用完了套接字来处理这些请求.
无论哪种方式,我们都会遇到扩展问题.我正试图找出解决问题的最佳方法.
一些想法:
Req.ServicePoint.BindIPEndPointDelegate
.这似乎很有希望,但是当你真正需要扩展时,你会怎么做?我不希望这个问题在2年内回归.serviceBus.maxConcurrentCalls
为1,一次只处理一条消息.也许我可以将其设置为相对较低的数字.现在,在某些时候,我们的队列填充速度将超过我们处理它们的速度,但此时答案是在我们的末端添加更多服务器.对于此类系统的推荐(可扩展!)设计的任何见解将不胜感激!
我想我已经找到了解决方案.我过去一直在运行这些变化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"));
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我们有一个静态实例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();
Run Code Online (Sandbox Code Playgroud)
我还没有真正深入分析套接字级别发生的事情(我不得不问问我们的IT人员是否能够在负载均衡器上看到这种流量),但我希望它只是保持单个套接字对我们的服务器开放,并在处理队列项时进行一堆HTTP调用.无论如何,无论它做什么似乎都在起作用.也许有人对如何改进有一些想法.
小智 6
我认为代码错误是因为: using (var client = new HttpClient())
引用自不正确的实例化反模式:
这种技术不可扩展。为每个用户请求创建一个新的 HttpClient 对象。在高负载下,Web 服务器可能会耗尽可用套接字的数量。
如果您在专用 Web 应用程序上使用消耗计划而不是函数,则#3 或多或少会立即发生。函数将检测到您有一个很大的消息队列,并将添加实例,直到队列长度稳定。
maxConcurrentCalls
仅适用于每个实例,允许您限制每个实例的并发数。基本上,您的处理速率是maxConcurrentCalls * instanceCount
。
控制全局吞吐量的唯一方法是在您选择的大小的专用 Web 应用程序上使用函数。每个应用程序都会轮询队列并根据需要获取工作。
最佳的扩展解决方案将改善 123.123.123.123 上的负载平衡,以便它可以处理来自向上/向下扩展的函数的任意数量的请求,以满足队列压力。
保持活动状态对于持久连接很有用,但函数执行不被视为持久连接。将来,我们将尝试向 Functions 添加“自带绑定”,这样您就可以根据需要实现连接池。
归档时间: |
|
查看次数: |
7263 次 |
最近记录: |