如何以高吞吐量同时读取多个EventHub分区?

时间:2016-02-27 17:46:36

标签: c# partition azure-eventhub

我的一个角色实例需要同时从20-40个EventHub分区读取数据(上下文:这是我们的内部虚拟分区方案--20-40个分区代表横向扩展单元)。

在我的原型中,我使用下面的代码。通过我获得最大8 MBPS的吞吐量。因为如果我多次运行相同的控制台,我的吞吐量(perfmon计数器)相应地相乘,那么我认为这既不是VM网络限制也不是EventHub服务端限制。

我想知道我是否在这里正确创建了客户......

谢谢! 扎基

        const string EventHubName = "...";
        const string ConsumerGroupName = "...";

        var connectionStringBuilder = new ServiceBusConnectionStringBuilder();
        connectionStringBuilder.SharedAccessKeyName = "...";
        connectionStringBuilder.SharedAccessKey = "...";
        connectionStringBuilder.Endpoints.Add(new Uri("sb://....servicebus.windows.net/"));
        connectionStringBuilder.TransportType = TransportType.Amqp;

        var clientConnectionString = connectionStringBuilder.ToString();
        var eventHubClient = EventHubClient.CreateFromConnectionString(clientConnectionString, EventHubName);

        var runtimeInformation = await eventHubClient.GetRuntimeInformationAsync().ConfigureAwait(false);

        var consumerGroup = eventHubClient.GetConsumerGroup(ConsumerGroupName);

        var offStart = DateTime.UtcNow.AddMinutes(-10);
        var offEnd = DateTime.UtcNow.AddMinutes(-8);

        var workUnitManager = new WorkUnitManager(runtimeInformation.PartitionCount);

        var readers = new List<PartitionReader>();
        for (int i = 0; i < runtimeInformation.PartitionCount; i++)
        {
            var reader = new PartitionReader(
                consumerGroup,
                runtimeInformation.PartitionIds[i],
                i,
                offStart,
                offEnd,
                workUnitManager);
            readers.Add(reader);
        }

    internal async Task Read()
    {
        try
        {
            Console.WriteLine("Creating a receiver for '{0}' with offset {1}", this.partitionId, this.startOffset);
            EventHubReceiver receiver = await this.consumerGroup.CreateReceiverAsync(this.partitionId, this.startOffset).ConfigureAwait(false);
            Console.WriteLine("Receiver for '{0}' has been created.", this.partitionId);

            var stopWatch = new Stopwatch();
            stopWatch.Start();

            while (true)
            {
                var message =
                    (await receiver.ReceiveAsync(1, TimeSpan.FromSeconds(10)).ConfigureAwait(false)).FirstOrDefault();
                if (message == null)
                {
                    continue;
                }

                if (message.EnqueuedTimeUtc >= this.endOffset)
                {
                    break;
                }

                this.processor.Push(this.partitionIndex, message);
            }

            this.Duration = TimeSpan.FromMilliseconds(stopWatch.ElapsedMilliseconds);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            throw;
        }
    }

2 个答案:

答案 0 :(得分:1)

您提供的上述代码段实际上是:创建1个连接到ServiceBus服务,然后在一个连接上运行所有接收器(在protocl级别,基本上,在同一连接上创建多个Amqp链接< /强>)。

或者 - 为了实现接收操作的高吞吐量,您需要创建多个连接并将 接收器映射到连接比率 微调您的吞吐量。这就是在多个进程中运行上述代码时会发生的情况。

以下是:

您需要在.Net客户端SDK API和MessagingFactory级别的代码中使用一层 - 您可以从每个EventHubClient的1个MessagingFactory 开始。 MessagingFactory就是一个 - 它代表1连接到EventHubs服务。用于为每个EventHubClient创建专用连接的代码:


var connStr = new ServiceBusConnectionStringBuilder("Endpoint=sb://servicebusnamespacename.servicebus.windows.net/;SharedAccessKeyName=saskeyname;SharedAccessKey=sakKey");
connStr.TransportType = TransportType.Amqp;
var msgFactory = MessagingFactory.CreateFromConnectionString(connStr.ToString());
var ehClient = msgFactory.CreateEventHubClient("teststream");
  • 我刚刚在我的示例中添加了connStr,以强调将 TransportType分配给Amqp

您将最终与传出端口 5671 建立多个连接:enter image description here

如果你使用每个EventHubClient的1个MessagingFactory(或合理的比例)重写你的代码 - 你们都已经设置好了(在你的代码中 - 你需要将EventHubClient创建移动到Reader)!

创建多个连接时需要考虑的唯一额外条件是Bill - only 100 connections are included (including senders and receivers) in basic sku。我猜你已经达到了标准(因为你有> 1个TU) - 它提供了包中包含的1000个连接 - 所以不必担心 - 但只提一下这种情况。

〜SREE

答案 1 :(得分:0)

一个好的选择是为每个分区创建一个任务。 这是我的实现的副本,它能够处理每个分区每秒2.5k消息的速率。此费率也与您的下游速度有关。

    static void EventReceiver()
    {
        for (int i = 0; i <= EventHubPartitionCount; i++)
        {
            Task.Factory.StartNew((state) =>
            {
                Console.WriteLine("Starting worker to process partition: {0}", state);

                var factory = MessagingFactory.Create(ServiceBusEnvironment.CreateServiceUri("sb", "tests-eventhub", ""), new MessagingFactorySettings()
                {
                    TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("Listen", "PGSVA7L="),
                    TransportType = TransportType.Amqp
                });

                var client = factory.CreateEventHubClient("eventHubName");
                var group = client.GetConsumerGroup("customConsumer");

                Console.WriteLine("Group: {0}", group.GroupName);

                var receiver = group.CreateReceiver(state.ToString(), DateTime.Now);


                while (true)
                {
                    if (cts.IsCancellationRequested)
                    {
                        receiver.Close();
                        break;
                    }

                    var messages = receiver.Receive(20);
                    messages.ToList().ForEach(aMessage =>
                    {                            
                         // Process your event
                    });

                    Console.WriteLine(counter);
                }
            }, i);
        }
    }