Azure EventHubs 初始化性能和 WebApi2

Azure EventHubs inizialization performances and WebApi2

我有一个 WebApi2 控制器,它接收来自 JavaScript 的 XmlHttpRequests。

我每秒对 api 调用 +500 次,任何请求都执行一些快速计算,然后我创建一个 Azure 存储队列(不是服务总线队列),传入一个序列化对象供以后处理. 直到这里一切正常,问题是 10-15% 的时间,仅初始化存储队列并添加 20k JSON 消息需要 500 毫秒到 2 秒之间的时间。 我将请求分成 10 个不同的队列,但问题仍然存在,而且似乎与流量无关,基本上有时队列会卡在创建过程中并减慢速度。

我已经禁用了 Nagle 和 Expect100Continue。

我想通过使用 EventHUbs 来转换这个架构,因为我的情况可能需要事件摄取器而不是简单的队列,需要最大速度。

但是 EventHub 的初始化也有同样的问题!启动到接收一条消息有时需要2、3秒,平均400ms。

我用秒表测了速度。

这是我在 API 控制器中的代码:

  var eventHubClient = StorageHelpers.InitializeEventHub("name", "Send");
                           await eventHubClient.SendAsync(new EventData(Encoding.UTF8.GetBytes(QueueSerialized)));

InizializeEventHub 所在位置:

        public static EventHubClient InitializeEventHub(string eventHubName, string type)
    {
        string connectionString = RoleEnvironment.GetConfigurationSettingValue("Hub"+type+eventHubName);
        return EventHubClient.CreateFromConnectionString(connectionString, eventHubName);}

该服务使用云服务托管在 Azure 上,托管在 ServiceBus 和存储的同一位置 (WestUS)。

我的问题是:

如果有任何关于此事的帮助,我将不胜感激,如果有某种方法可以加快初始化和 AddMessageAsync 操作,我什至可以 return 存储队列。

谢谢

  1. 不确定,我从不费心去计算它的时间,因为如果你重复使用它,它就不会像其他情况下那么重要了。鉴于 network connection gets reused
  2. 似乎太长了
  3. 是的。
  4. 这取决于你所说的缓存是什么意思。如果您的意思是序列化并保存在内存中的某个地方,那么不会。如果你的意思是放入一个 ConcurrentBag(像池一样使用它),那么肯定。

如果您每 20KB 每服务器每秒发出 >500 个请求,那么您应该确认您设置了足够的吞吐量单位,因为这是 >10MB/秒的流入,至少需要 10 个吞吐量单位。节流可以解释延迟问题。另一件要检查的事情是初始化的哪些组件需要时间,例如我从未对 GetConfigurationSettingValue 进行基准测试,它可能不会被缓存。

但假设 none 是问题所在,那么问题是您需要做什么才能让它变快?您当然可以重用 EventHubClient 或您自己创建的对象来处理创建时间。不太连接到 WebAPI 简单的方法是简单地拥有一个包含实例的静态变量(可能在 Lazy). When reusing this you should know that the EventHubClient is not officially threadsafe (though Send appears to be in reality) which means you'll need to manage it. But a single EventHubClient or multiple ones sharing the same network connection may not work out for you with 10MB/s per server. In that case I direct your attention to this portion of the documentation:

内部进行构造函数初始化

Finally, it is also possible to create an EventHubClient object from a MessagingFactory instance, as shown in the following example.

var factory = MessagingFactory.CreateFromConnectionString("your_connection_string"); var client = factory.CreateEventHubClient("MyEventHub");

It is important to note that additional EventHubClient objects created from a messaging factory instance will reuse the same underlying TCP connection. Therefore, these objects have a client-side limit on throughput. The Create method reuses a single messaging factory. If you need very high throughput from a single sender, then you can create multiple message factories and one EventHubClient object from each messaging factory.

如果您正在这样做,那么我强烈建议汇集 them/writing 您自己的多路复用器。

伟大的Qstn!这是我的看法:

  1. 在 Azure 最繁忙的规模单元之一(如美国西部)- 400 毫秒数量级。 听起来确实是事件中心发送延迟的可能数字。您正在寻找的平均延迟是多少? 第一个调用需要 2-3 秒的时间来创建连接,尤其是 SSL 协商。这些在该地区的各种 Azure 服务之间没有显着差异。这次只有前几个电话需要。所有后续调用应按 millis 的顺序。 EventHubClient.Send API (),它是为HighAvailability设计的,首先将消息发送到一个高可用的ServiceBus Gateway,然后再转发到其中一个可用的EventHub分区——使其对发送操作高度可用。这确实增加了网关在第一次发送时发现分区的少量初始化成本。比方说,如果您的分区数是 4,那么您对该 EventHub 的前 4 个 Send 调用可能需要更高的延迟 - 从它们开始 - 它是高性能的。
  2. 只要您与之通话的 EventHub 是相同的,您就可以在 Web 中共享 EventHubClientAPI。每个 EventHubClient 都与一个连接相关联。然而,在 EventHub .net SDK 中,只要 2 个 EventHubClient 的连接字符串相同 - 连接将被重新使用。 这里的一个优化 - 如果您的流量较少并且通过拥有更多事件中心而拥有扇出架构:即,如果您的场景有多个事件中心并且您的所有事件中心都在一个命名空间中并且想要使用 1 个 EventHubClient 对象(其中意味着每个 webapi 进程只有 1 个套接字)发送到 EventHubs 服务,您可以使用 MessagingFactory(具有命名空间级别 SasKey)来创建 EventHubClient。

var msgFactory = MessagingFactory.CreateFromConnectionString(@"Endpoint=amqps://---namespaceName----.servicebus.windows.net;SharedAccessKeyName=---SasKeyName----;SharedAccessKey=----SasKey----"); var ehClient = msgFactory.CreateEventHubClient("----eventHubName----");

  1. 您可以考虑缓存 EventHubClient 对象。它可以节省几行客户端代码执行来从缓存中获取 MessagingFactory(它保存对连接的引用)。

喂! 斯里

我最终得到了一个非常简单的解决方案。 EventHubs 和 StorageQueues 都需要时间来初始化,尤其是 EventHubs 在向流中添加消息时通常很慢。现在 300 毫秒在 99.99% 的情况下并不慢,但在我的情况下是这样。

StorageQueue 超级便宜、快速且简单,但添加消息的速度非常慢。 经过数小时的基准测试和其他解决方案检查,如 Redis Pub/Sub,我最终使用了 StorageQueues,只是没有等待异步调用。

所以标准调用是

await queue.AddMessageAsync(message);

等待部分是问题所在,如果任务没有返回,WebApi 无法 return。应该是即刻即弃,但事实并非如此。

我解决了不等待调用的问题,使用变量隐藏了警告

var nowait = queue.AddMessageAsync(message);

队列中的插入在任何情况下都是-立即-,没有消息丢失。