Example #1
0
        /// <summary>
        /// Gets an observable sequence of any changes to the Fetcher for a specific topic/partition.
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="partitionId"></param>
        /// <param name="consumerConfiguration"></param>
        /// <returns></returns>
        internal IObservable <Fetcher> GetFetcherChanges(string topic, int partitionId, ConsumerConfiguration consumerConfiguration)
        {
            return(PartitionStateChanges
                   .Where(t => t.Topic == topic && t.PartitionId == partitionId)
                   .Select(state =>
            {
                // if the state is not ready, return NULL for the fetcher.
                if (!state.ErrorCode.IsSuccess())
                {
                    return (Fetcher)null;
                }

                // get or create the correct Fetcher for this topic/partition
                var broker = FindBrokerMetaForPartitionId(state.Topic, state.PartitionId);

                var fetcher = _activeFetchers.GetOrAdd(broker, b =>
                {
                    // all new fetchers need to be "watched" for errors.
                    var f = new Fetcher(this, b, _protocol, consumerConfiguration, _cancel.Token);
                    // subscribe to error and complete notifications, and remove from active fetchers
                    f.ReceivedMessages.Subscribe(_ => { },
                                                 err =>
                    {
                        _log.Warn("Received error from fetcher {0}. Removing from active fetchers.", f);
                        Fetcher ef;
                        _activeFetchers.TryRemove(broker, out ef);
                    },
                                                 () =>
                    {
                        _log.Info("Received complete from fetcher {0}. Removing from active fetchers.", f);
                        Fetcher ef;
                        _activeFetchers.TryRemove(broker, out ef);
                    });
                    return f;
                });

                return fetcher;
            })
                   .Do(f => _log.Debug("GetFetcherChanges returning {1} fetcher {0}", f, f == null ? "null" : "new"),
                       ex => _log.Error(ex, "GetFetcherChages saw ERROR returning new fetcher."),
                       () => _log.Error("GetFetcherChanges saw COMPLETE from returning new fetcher.")));
        }
Example #2
0
        public async void SpeedTest()
        {
            int _pageViewBatchSize = 10 * 1000;
            TimeSpan _batchTime = TimeSpan.FromSeconds(10);

            var conf = new ConsumerConfiguration("kafkadev-01.lv.ntent.com", 
                "vsw.avrodto.addelivery.activitylogging.pageview", 
                new StartPositionTopicStart(), 
                useFlowControl: true,
                highWatermark: _pageViewBatchSize * 5,
                lowWatermark: _pageViewBatchSize * 2
            );
            var consumer = new Consumer(conf);
            var count = 0L;
            var lastCount = count;
            
            var msg = consumer.OnMessageArrived.Publish().RefCount();
            msg.
                Buffer(_batchTime, _pageViewBatchSize).
                Subscribe(_ =>
                {
                    Interlocked.Add(ref count, _.Count);
                    consumer.Ack(_.Count);
                    //consumer.Ack();
                });
            
            var interval = TimeSpan.FromSeconds(5);
            var lastTime = DateTime.Now;
            Observable.Interval(interval).Subscribe(_ => {
                var c = count;
                var now = DateTime.Now;
                var time = now - lastTime;
                var speed = (c - lastCount)/time.TotalSeconds;
                lastCount = c;
                lastTime = now;
                Console.WriteLine("{0}msg/sec #{1}", speed, c);
            });

            await consumer.IsConnected;

            await msg.Timeout(TimeSpan.FromSeconds(30));
            Console.WriteLine("Complete {0}", count);
        }
Example #3
0
        /// <summary>
        /// Create a new consumer using the specified configuration. See @ConsumerConfiguration
        /// </summary>
        /// <param name="consumerConfig"></param>
        public Consumer(ConsumerConfiguration consumerConfig)
        {
            Configuration = consumerConfig;
            _cluster = new Cluster(consumerConfig.SeedBrokers);
            _cluster.OnThreadHang += e => OnMessageArrivedInput.OnError(e);


            // Low/high watermark implementation
            FlowControl = _flowControlInput.
                Scan(1, (last, current) =>
                {
                    if (current < Configuration.LowWatermark)
                        return 1;
                    if (current > Configuration.HighWatermark)
                        return -1;
                    return last; // While in between watermarks, carry over previous on/off state
                }).
                    DistinctUntilChanged().
                    Select(i => i > 0).
                    Do(f =>FlowControlEnabled = f).
                    Do(f => EtwTrace.Log.ConsumerFlowControl(f ? 1 : 0));

            var onMessage = Observable.Create<ReceivedMessage>(observer =>
            {
                if (Interlocked.CompareExchange(ref _haveSubscriber, 1, 0) == 1)
                    throw new InvalidOperationException("Only one subscriber is allowed. Use OnMessageArrived.Publish().RefCount()");

                // Relay messages from partition to consumer's output
                // Ensure that only single subscriber is allowed because it is important to count
                // outstanding messaged consumed by user
                OnMessageArrivedInput.Subscribe(observer);

                //
                // It is not possible to wait for completion of partition resolution process, so start it asynchronously.
                // This means that OnMessageArrived.Subscribe() will complete when consumer is not actually connected yet.
                //
                Task.Run(async () =>
                {
                    try 
                    {
                        await _cluster.ConnectAsync();
                        await SubscribeClient();
                        EtwTrace.Log.ConsumerStarted(GetHashCode(), Topic);
                        _connectionComplete.TrySetResult(true);

                        // check that we actually got any partitions subscribed
                        if (_topicPartitions.Count == 0)
                            OnMessageArrivedInput.OnCompleted();
                    }
                    catch (Exception e)
                    {
                        _connectionComplete.TrySetException(e);
                        OnMessageArrivedInput.OnError(e);
                    }
                });

                // upon unsubscribe
                return Disposable.Create(() => _partitionsSubscription.Values.ForEach(s=>s.Dispose()));
            });

            if (Configuration.UseFlowControl) { 
            // Increment counter of messages sent for processing
                onMessage = onMessage.Do(msg =>
                {
                    var count = Interlocked.Increment(ref _outstandingMessageProcessingCount);
                    _flowControlInput.OnNext(count);
                });
            }

            // handle stop condition
            onMessage = onMessage.Do(message =>
            {
                // check if this partition is done per the condition passed in configuration. If so, unsubscribe it.
                bool partitionDone = (Configuration.StopPosition.IsPartitionConsumingComplete(message));
                IDisposable partitionSubscription;
                if (partitionDone && _partitionsSubscription.TryGetValue(message.Partition, out partitionSubscription))
                {
                    _partitionsSubscription.Remove(message.Partition);
                    // calling Dispose here will cause the OnTopicPartitionComplete method to be called when it is completed.
                    partitionSubscription.Dispose();
                }
            });

            OnMessageArrived = onMessage.
                // isolate driver from user code misbehave
                ObserveOn(Configuration.OutgoingScheduler);

            // If permanent error within any single partition, fail the whole consumer (intentionally). 
            // Do not try to keep going (failfast principle).
            _cluster.PartitionStateChanges.
                Where(s => s.ErrorCode.IsPermanentFailure()).
                Subscribe(state => OnMessageArrivedInput.OnError(new PartitionFailedException(state.Topic, state.PartitionId, state.ErrorCode)));
        }
Example #4
0
        /// <summary>
        /// Gets an observable sequence of any changes to the Fetcher for a specific topic/partition. 
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="partitionId"></param>
        /// <param name="consumerConfiguration"></param>
        /// <returns></returns>
        internal IObservable<Fetcher> GetFetcherChanges(string topic, int partitionId, ConsumerConfiguration consumerConfiguration)
        {
            return PartitionStateChanges
                .Where(t => t.Topic == topic && t.PartitionId == partitionId)
                .Select(state =>
                {
                    // if the state is not ready, return NULL for the fetcher.
                    if (!state.ErrorCode.IsSuccess())
                        return (Fetcher)null;

                    // get or create the correct Fetcher for this topic/partition
                    var broker = FindBrokerMetaForPartitionId(state.Topic, state.PartitionId);

                    var fetcher = _activeFetchers.GetOrAdd(broker, b =>
                    {
                        // all new fetchers need to be "watched" for errors.
                        var f = new Fetcher(this, b, _protocol, consumerConfiguration, _cancel.Token);
                        // subscribe to error and complete notifications, and remove from active fetchers
                        f.ReceivedMessages.Subscribe(_ => { },
                            err =>
                            {
                                _log.Warn("Received error from fetcher {0}. Removing from active fetchers.", f);
                                Fetcher ef;
                                _activeFetchers.TryRemove(broker, out ef);
                            },
                            () =>
                            {
                                _log.Info("Received complete from fetcher {0}. Removing from active fetchers.", f);
                                Fetcher ef;
                                _activeFetchers.TryRemove(broker, out ef);
                            });
                        return f;
                    });

                    return fetcher;

                })
                .Do(f => _log.Debug("GetFetcherChanges returning {1} fetcher {0}", f, f == null ? "null" : "new"),
                    ex => _log.Error(ex, "GetFetcherChages saw ERROR returning new fetcher."),
                    () => _log.Error("GetFetcherChanges saw COMPLETE from returning new fetcher."));
        }
Example #5
0
        /// <summary>
        /// Create a new consumer using the specified configuration. See @ConsumerConfiguration
        /// </summary>
        /// <param name="consumerConfig"></param>
        public Consumer(ConsumerConfiguration consumerConfig)
        {
            Configuration          = consumerConfig;
            _cluster               = new Cluster(consumerConfig.SeedBrokers);
            _cluster.OnThreadHang += e => OnMessageArrivedInput.OnError(e);


            // Low/high watermark implementation
            FlowControl = _flowControlInput.
                          Scan(1, (last, current) =>
            {
                if (current < Configuration.LowWatermark)
                {
                    return(1);
                }
                if (current > Configuration.HighWatermark)
                {
                    return(-1);
                }
                return(last);    // While in between watermarks, carry over previous on/off state
            }).
                          DistinctUntilChanged().
                          Select(i => i > 0).
                          Do(f => FlowControlEnabled = f).
                          Do(f => EtwTrace.Log.ConsumerFlowControl(f ? 1 : 0));

            var onMessage = Observable.Create <ReceivedMessage>(observer =>
            {
                if (Interlocked.CompareExchange(ref _haveSubscriber, 1, 0) == 1)
                {
                    throw new InvalidOperationException("Only one subscriber is allowed. Use OnMessageArrived.Publish().RefCount()");
                }

                // Relay messages from partition to consumer's output
                // Ensure that only single subscriber is allowed because it is important to count
                // outstanding messaged consumed by user
                OnMessageArrivedInput.Subscribe(observer);

                //
                // It is not possible to wait for completion of partition resolution process, so start it asynchronously.
                // This means that OnMessageArrived.Subscribe() will complete when consumer is not actually connected yet.
                //
                Task.Run(async() =>
                {
                    try
                    {
                        await _cluster.ConnectAsync();
                        await SubscribeClient();
                        EtwTrace.Log.ConsumerStarted(GetHashCode(), Topic);
                        _connectionComplete.TrySetResult(true);

                        // check that we actually got any partitions subscribed
                        if (_topicPartitions.Count == 0)
                        {
                            OnMessageArrivedInput.OnCompleted();
                        }
                    }
                    catch (Exception e)
                    {
                        _connectionComplete.TrySetException(e);
                        OnMessageArrivedInput.OnError(e);
                    }
                });

                // upon unsubscribe
                return(Disposable.Create(() => _partitionsSubscription.Values.ForEach(s => s.Dispose())));
            });

            if (Configuration.UseFlowControl)
            {
                // Increment counter of messages sent for processing
                onMessage = onMessage.Do(msg =>
                {
                    var count = Interlocked.Increment(ref _outstandingMessageProcessingCount);
                    _flowControlInput.OnNext(count);
                });
            }

            // handle stop condition
            onMessage = onMessage.Do(message =>
            {
                // check if this partition is done per the condition passed in configuration. If so, unsubscribe it.
                bool partitionDone = (Configuration.StopPosition.IsPartitionConsumingComplete(message));
                IDisposable partitionSubscription;
                if (partitionDone && _partitionsSubscription.TryGetValue(message.Partition, out partitionSubscription))
                {
                    _partitionsSubscription.Remove(message.Partition);
                    // calling Dispose here will cause the OnTopicPartitionComplete method to be called when it is completed.
                    partitionSubscription.Dispose();
                }
            });

            OnMessageArrived = onMessage.
                               // isolate driver from user code misbehave
                               ObserveOn(Configuration.OutgoingScheduler);

            // If permanent error within any single partition, fail the whole consumer (intentionally).
            // Do not try to keep going (failfast principle).
            _cluster.PartitionStateChanges.
            Where(s => s.ErrorCode.IsPermanentFailure()).
            Subscribe(state => OnMessageArrivedInput.OnError(new PartitionFailedException(state.Topic, state.PartitionId, state.ErrorCode)));
        }