예제 #1
0
        public async Task ConnectAsync()
        {
            // if we've already connected, don't do it again.
            if (IsConnected)
            {
                return;
            }

            await _cluster.Scheduler.Ask(async() =>
            {
                await _sync.WaitAsync();

                try
                {
                    if (IsConnected)
                    {
                        return;
                    }

                    _log.Debug("Connecting producer {1} for topic {0}", Topic, _id);
                    EtwTrace.Log.ProducerStarting(Topic, _id);
                    _sendMessagesSubject = new Subject <Message>();

                    if (_cluster.State != Cluster.ClusterState.Connected)
                    {
                        _log.Debug("Connecting cluster");
                        await _cluster.ConnectAsync();
                        _log.Debug("Connected cluster");
                    }

                    // Recovery: subscribe to partition offline/online events
                    _cluster.PartitionStateChanges.
                    Where(p => p.Topic == Configuration.Topic).
                    Synchronize(_allPartitionQueues).
                    Subscribe(p =>
                    {
                        PartitionQueueInfo queue;
                        if (!_allPartitionQueues.TryGetValue(p.PartitionId, out queue))
                        {
                            queue = new PartitionQueueInfo(Configuration.SendBuffersInitialSize)
                            {
                                Partition = p.PartitionId
                            };
                            _allPartitionQueues.Add(p.PartitionId, queue);
                        }

                        _log.Info("Detected change in topic/partition '{0}'/{1}/{2} IsOnline {3}->{4}",
                                  Configuration.Topic,
                                  p.PartitionId,
                                  p.ErrorCode,
                                  queue.IsOnline,
                                  p.ErrorCode.IsSuccess());

                        queue.IsOnline = p.ErrorCode.IsSuccess();
                        _queueEventWaitHandler.Set();
                    });

                    // get all of the partitions for this topic. Allows the MessagePartitioner to select a partition.
                    var topicPartitions = await _cluster.GetOrFetchMetaForTopicAsync(Configuration.Topic);
                    _log.Debug("Producer found {0} partitions for '{1}'", topicPartitions.Length, Configuration.Topic);

                    _sendMessagesSubject.
                    Do(msg => msg.PartitionId = Configuration.Partitioner.GetMessagePartition(msg, topicPartitions).Id).
                    Buffer(Configuration.BatchFlushTime, Configuration.BatchFlushSize).
                    Where(b => b.Count > 0).
                    Select(msgs => msgs.GroupBy(msg => msg.PartitionId)).
                    ObserveOn(_cluster.Scheduler).
                    Subscribe(partitionGroups =>
                    {
                        foreach (var batch in partitionGroups)
                        {
                            // partition queue might be created if metadata broadcast fired already
                            PartitionQueueInfo queue;
                            if (!_allPartitionQueues.TryGetValue(batch.Key, out queue))
                            {
                                queue = new PartitionQueueInfo(Configuration.SendBuffersInitialSize)
                                {
                                    Partition = batch.Key
                                };
                                _allPartitionQueues.Add(batch.Key, queue);
                                queue.IsOnline = topicPartitions.First(p => p.Id == batch.Key).ErrorCode.IsSuccess();
                                _log.Debug("{0} added new partition queue", this);
                            }

                            // now queue them up
                            var batchAr = batch.ToArray();

                            // make sure we have space.
                            if (queue.Queue.Size + batchAr.Length > queue.Queue.Capacity)
                            {
                                // try to increase the capacity.
                                if (Configuration.AutoGrowSendBuffers)
                                {
                                    var growBy = Math.Max(queue.Queue.Capacity / 2 + 1, 2 * batchAr.Length);
                                    _log.Warn("Capacity of send buffer with size {3} not large enough to accept {2} new messages. Increasing capacity from {0} to {1}", queue.Queue.Capacity, queue.Queue.Capacity + growBy, batchAr.Length, queue.Queue.Size);

                                    queue.Queue.Capacity += growBy;
                                }
                                else
                                {
                                    // we're full and not allowed to grow. Throw the batch back to the caller, and continue on.
                                    var msg = string.Format("Send Buffer Full for partition {0}. ",
                                                            Cluster.PartitionStateChanges.Where(
                                                                ps => ps.Topic == Configuration.Topic && ps.PartitionId == queue.Partition)
                                                            .Take(1)
                                                            .Wait());
                                    _log.Error(msg);
                                    if (OnPermError != null)
                                    {
                                        OnPermError(
                                            new Exception(msg)
                                            , batchAr);
                                    }
                                    continue;
                                }
                            }

                            // we have the space, add to the queue
                            queue.Queue.Put(batchAr);

                            if (_log.IsDebugEnabled)
                            {
                                _log.Debug("Enqueued batch of size {0} for topic '{1}' partition {2}",
                                           batchAr.Length, Configuration.Topic, batch.Key);
                            }

                            // After batch enqueued, send wake up signal to sending queue
                            _queueEventWaitHandler.Set();
                        }
                    }, e => _log.Fatal(e, "Error in _sendMessagesSubject pipeline"),
                              () => _log.Debug("_sendMessagesSubject complete")
                              );

                    // start the send loop task
                    _cluster.Scheduler.Schedule(() => {
                        _sendLoopTask = SendLoop().
                                        ContinueWith(t =>
                        {
                            if (t.IsFaulted)
                            {
                                _log.Fatal(t.Exception, "SendLoop failed");
                            }
                            else
                            {
                                _log.Debug("SendLoop complete with status: {0}", t.Status);
                            }
                        });
                    });
                    _log.Debug("Connected");
                    EtwTrace.Log.ProducerStarted(Topic, _id);
                }
                catch (Exception e)
                {
                    _log.Error(e, "Exception during connect");
                    EtwTrace.Log.ProducerError(e.Message, _id);
                    throw;
                }
                finally
                {
                    _log.Debug("#{0} Releasing Producer Semaphore.", _id);
                    _sync.Release();
                }
            }).ConfigureAwait(false);
        }
예제 #2
0
        public async Task ConnectAsync()
        {
            // if we've already connected, don't do it again.
            if (IsConnected)
                return;

            await await _cluster.Scheduler.Ask(async () =>
            {
                await _sync.WaitAsync();

                try
                {
                    if (IsConnected)
                        return;

                    _log.Debug("Connecting producer {1} for topic {0}", Topic, _id);
                    EtwTrace.Log.ProducerStarting(Topic, _id);
                    _sendMessagesSubject = new Subject<Message>();

                    if (_cluster.State != Cluster.ClusterState.Connected)
                    {
                        _log.Debug("Connecting cluster");
                        await _cluster.ConnectAsync();
                        _log.Debug("Connected cluster");
                    }

                    // Recovery: subscribe to partition offline/online events
                    _partitionStateSubsctiption = _cluster.PartitionStateChanges.
                        Where(p => p.Topic == Configuration.Topic).
                        Synchronize(_allPartitionQueues).
                        Subscribe(p =>
                        {
                            PartitionQueueInfo queue;
                            if (!_allPartitionQueues.TryGetValue(p.PartitionId, out queue))
                            {
                                queue = new PartitionQueueInfo(Configuration.SendBuffersInitialSize) { Partition = p.PartitionId };
                                _allPartitionQueues.Add(p.PartitionId, queue);
                                _queueSizeEvents.OnNext(new QueueResizeInfo{PartitionId = p.PartitionId, Size = queue.Queue.Size, Capacity = queue.Queue.Capacity});
                            }

                            _log.Info("Detected change in topic/partition '{0}'/{1}/{2} IsOnline {3}->{4}", 
                                Configuration.Topic, 
                                p.PartitionId, 
                                p.ErrorCode,
                                queue.IsOnline, 
                                p.ErrorCode.IsSuccess());

                            queue.IsOnline = p.ErrorCode.IsSuccess();
                            _queueEventWaitHandler.Set();
                        });

                    // get all of the partitions for this topic. Allows the MessagePartitioner to select a partition.
                    var topicPartitions = await _cluster.GetOrFetchMetaForTopicAsync(Configuration.Topic);
                    _log.Debug("Producer found {0} partitions for '{1}'", topicPartitions.Length, Configuration.Topic);

                    _sendMessagesSubject.
                        Do(msg => msg.PartitionId = Configuration.Partitioner.GetMessagePartition(msg, topicPartitions).Id).
                        Buffer(Configuration.BatchFlushTime, Configuration.BatchFlushSize).
                        Where(b => b.Count > 0).
                        Select(msgs => msgs.GroupBy(msg => msg.PartitionId)).
                        ObserveOn(_cluster.Scheduler).
                        Subscribe(partitionGroups =>
                        {
                            foreach (var batch in partitionGroups)
                            {
                                // partition queue might be created if metadata broadcast fired already
                                PartitionQueueInfo queue;
                                if (!_allPartitionQueues.TryGetValue(batch.Key, out queue))
                                {
                                    queue = new PartitionQueueInfo(Configuration.SendBuffersInitialSize) { Partition = batch.Key };
                                    _allPartitionQueues.Add(batch.Key, queue);
                                    queue.IsOnline = topicPartitions.First(p => p.Id == batch.Key).ErrorCode.IsSuccess();
                                    _log.Debug("{0} added new partition queue", this);
                                    _queueSizeEvents.OnNext(new QueueResizeInfo { PartitionId = queue.Partition, Size = queue.Queue.Size, Capacity = queue.Queue.Capacity });
                                }

                                // now queue them up
                                var batchAr = batch.ToArray();

                                // make sure we have space.
                                if (queue.Queue.Size + batchAr.Length > queue.Queue.Capacity)
                                {
                                    // try to increase the capacity.
                                    if (Configuration.AutoGrowSendBuffers)
                                    {
                                        var growBy = Math.Max(queue.Queue.Capacity/2 + 1, 2 * batchAr.Length);
                                        _log.Warn("Capacity of send buffer with size {3} not large enough to accept {2} new messages. Increasing capacity from {0} to {1}", queue.Queue.Capacity, queue.Queue.Capacity+growBy, batchAr.Length, queue.Queue.Size);

                                        queue.Queue.Capacity += growBy;
                                        _queueSizeEvents.OnNext(new QueueResizeInfo { PartitionId = queue.Partition, Size = queue.Queue.Size, Capacity = queue.Queue.Capacity });
                                    }
                                    else
                                    {
                                        // we're full and not allowed to grow. Throw the batch back to the caller, and continue on.
                                        var msg = string.Format("Send Buffer Full for partition {0}. ",
                                                    Cluster.PartitionStateChanges.Where(
                                                        ps => ps.Topic == Configuration.Topic && ps.PartitionId == queue.Partition)
                                                        .Take(1)
                                                        .Wait());
                                        _log.Error(msg);
                                        if (OnPermError != null)
                                            OnPermError(new Exception(msg), batchAr);
                                        continue;
                                    }
                                }

                                // we have the space, add to the queue
                                queue.Queue.Put(batchAr);

                                if (_log.IsDebugEnabled)
                                    _log.Debug("Enqueued batch of size {0} for topic '{1}' partition {2}",
                                        batchAr.Length, Configuration.Topic, batch.Key);

                                // After batch enqueued, send wake up signal to sending queue
                                _queueEventWaitHandler.Set();
                            }
                        }, e => _log.Fatal(e, "Error in _sendMessagesSubject pipeline"),
                            () => _log.Debug("_sendMessagesSubject complete")
                        );

                    // start the send loop task
                    _cluster.Scheduler.Schedule(() => {
                        _sendLoopTask = SendLoop().
                            ContinueWith(t =>
                            {
                                if (t.IsFaulted)
                                    _log.Fatal(t.Exception, "SendLoop failed");
                                else
                                    _log.Debug("SendLoop complete with status: {0}", t.Status);
                            });
                    });
                    _log.Debug("Connected");
                    EtwTrace.Log.ProducerStarted(Topic, _id);
                }
                catch (Exception e)
                {
                    _log.Error(e, "Exception during connect");
                    EtwTrace.Log.ProducerError(e.Message, _id);
					throw;
                }
                finally
                {
                    _log.Debug("#{0} Releasing Producer Semaphore.", _id);
                    _sync.Release();
                }
            }).ConfigureAwait(false);
        }