Esempio n. 1
0
        /// <summary>
        /// Selects a synchronous producer, for
        /// the specified broker id and calls the send API on the selected
        /// producer to publish the data to the specified broker partition.
        /// </summary>
        /// <param name="poolData">The producer pool request object.</param>
        /// <remarks>
        /// Used for multi-topic request
        /// </remarks>
        public override void Send(IEnumerable <ProducerPoolData <TData> > poolData)
        {
            this.EnsuresNotDisposed();
            Guard.NotNull(poolData, "poolData");
            Dictionary <int, List <ProducerPoolData <TData> > > distinctBrokers = poolData.GroupBy(
                x => x.BidPid.BrokerId, x => x)
                                                                                  .ToDictionary(x => x.Key, x => x.ToList());

            foreach (var broker in distinctBrokers)
            {
                Logger.DebugFormat(CultureInfo.CurrentCulture, "Fetching sync producer for broker id: {0}", broker.Key);
                ISyncProducer producer = this.syncProducers[broker.Key];
                IEnumerable <ProducerRequest> requests = broker.Value.Select(x => new ProducerRequest(
                                                                                 x.Topic,
                                                                                 x.BidPid.PartId,
                                                                                 new BufferedMessageSet(x.Data.Select(y => this.Serializer.ToMessage(y)))));
                Logger.DebugFormat(CultureInfo.CurrentCulture, "Sending message to broker {0}", broker.Key);
                if (requests.Count() > 1)
                {
                    producer.MultiSend(requests);
                }
                else
                {
                    producer.Send(requests.First());
                }
            }
        }
        /// <summary>
        /// Force get topic metadata and update
        /// </summary>
        public void UpdateInfo(short versionId, int correlationId, string clientId, string topic)
        {
            Logger.InfoFormat("Will update metadata for topic:{0}", topic);
            Guard.NotNullNorEmpty(topic, "topic");
            var shuffledBrokers = this.syncProducerPool.GetShuffledProducers();
            var i = 0;
            var hasFetchedInfo = false;

            while (i < shuffledBrokers.Count && !hasFetchedInfo)
            {
                ISyncProducer producer = shuffledBrokers[i++];

                try
                {
                    var topicMetadataRequest = TopicMetadataRequest.Create(new List <string>()
                    {
                        topic
                    }, versionId,
                                                                           correlationId, clientId);
                    var topicMetadataList = producer.Send(topicMetadataRequest);
                    var topicMetadata     = topicMetadataList.Any() ? topicMetadataList.First() : null;
                    if (topicMetadata != null)
                    {
                        if (topicMetadata.Error != ErrorMapping.NoError)
                        {
                            Logger.WarnFormat("Try get metadata of topic {0} from {1}({2}) . Got error: {3}", topic, producer.Config.BrokerId, producer.Config.Host, topicMetadata.Error.ToString());
                        }
                        else
                        {
                            this.topicPartitionInfo[topic] = topicMetadata;
                            this.topicPartitionInfoLastUpdateTime[topic] = DateTime.UtcNow;
                            Logger.InfoFormat("Will  Update  metadata info, topic {0} ", topic);

                            //TODO:  For all partitions which has metadata, here return the sorted list.
                            //But sometimes kafka didn't return metadata for all topics.
                            this.topicPartitionInfoList[topic] = topicMetadata.PartitionsMetadata.Select(m =>
                            {
                                Partition partition = new Partition(topic, m.PartitionId);
                                if (m.Leader != null)
                                {
                                    var leaderReplica = new Replica(m.Leader.Id, topic);
                                    partition.Leader  = leaderReplica;
                                    Logger.InfoFormat("Topic {0} partition {1} has leader {2}", topic,
                                                      m.PartitionId, m.Leader.Id);

                                    return(partition);
                                }

                                Logger.WarnFormat("Topic {0} partition {1} does not have a leader yet", topic,
                                                  m.PartitionId);

                                return(partition);
                            }
                                                                                                         ).OrderBy(x => x.PartId).ToList();;
                            hasFetchedInfo = true;
                            Logger.InfoFormat("Finish  Update  metadata info, topic {0}  Partitions:{1}  No leader:{2}", topic, this.topicPartitionInfoList[topic].Count, this.topicPartitionInfoList[topic].Where(r => r.Leader == null).Count());

                            //In very weired case, the kafka broker didn't return metadata of all broker. need break and retry.  https://issues.apache.org/jira/browse/KAFKA-1998
                            // http://qnalist.com/questions/5899394/topicmetadata-response-miss-some-partitions-information-sometimes
                            if (zkClient != null)
                            {
                                Dictionary <int, int[]> topicMetaDataInZookeeper = ZkUtils.GetTopicMetadataInzookeeper(this.zkClient, topic);
                                if (topicMetaDataInZookeeper != null && topicMetaDataInZookeeper.Any())
                                {
                                    topicDataInZookeeper[topic] = topicMetaDataInZookeeper;
                                    if (this.topicPartitionInfoList[topic].Count != topicMetaDataInZookeeper.Count)
                                    {
                                        Logger.ErrorFormat("NOT all partition has metadata.  Topic partition in zookeeper :{0} topics has partition metadata: {1}", topicMetaDataInZookeeper.Count, this.topicPartitionInfoList[topic].Count);
                                        throw new UnavailableProducerException(string.Format("Please make sure every partition at least has one broker running and retry again.   NOT all partition has metadata.  Topic partition in zookeeper :{0} topics has partition metadata: {1}", topicMetaDataInZookeeper.Count, this.topicPartitionInfoList[topic].Count));
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.ErrorFormat("Try get metadata of topic {0} from {1}({2}) . Got error: {3}", topic, producer.Config.BrokerId, producer.Config.Host, e.FormatException());
                }
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Send message of one broker.
        /// </summary>
        /// <param name="brokerId"></param>
        /// <param name="messagesPerTopic"></param>
        /// <returns></returns>
        private ProducerSendResult <IEnumerable <Tuple <TopicAndPartition, ProducerResponseStatus> > > Send(int brokerId, IDictionary <TopicAndPartition, BufferedMessageSet> messagesPerTopic)
        {
            try
            {
                if (brokerId < 0)
                {
                    throw new NoLeaderForPartitionException(
                              string.Format("No leader for some partition(s).  And it try write to on invalid broker {0}.  The assigned TopicAndPartition for the data is :{1} ", brokerId, messagesPerTopic.Any() ? messagesPerTopic.First().Key.ToString() : "(null)"));
                }
                if (messagesPerTopic.Any())
                {
                    var producerRequest = new ProducerRequest(NextCorrelationId,
                                                              this.producerConfig.ClientId,
                                                              this.producerConfig.RequiredAcks,
                                                              this.producerConfig.AckTimeout,
                                                              messagesPerTopic);
                    ISyncProducer syncProducer = null;
                    try
                    {
                        syncProducer = this.syncProducerPool.GetProducer(brokerId);
                    }
                    catch (UnavailableProducerException e)
                    {
                        Logger.Error(e.Message);
                        // When initializing producer pool, some broker might be unavailable, and now it is healthy and is leader for some partitions.
                        // A new producer should be added to the pool, creating a TCP connection to the broker.
                        var broker =
                            this.brokerPartitionInfo.GetBrokerPartitionLeaders(messagesPerTopic.Keys.First().Topic)
                            .Values.FirstOrDefault(b => b.Id == brokerId);
                        if (broker != null)
                        {
                            this.syncProducerPool.AddProducer(broker);
                            syncProducer = this.syncProducerPool.GetProducer(brokerId);
                        }
                    }

                    if (producerConfig.Verbose)
                    {
                        Logger.DebugFormat("Kafka producer before sent messages for topics {0} to broker {1}", messagesPerTopic, brokerId);
                    }
                    ProducerResponse response = syncProducer.Send(producerRequest);
                    if (this.producerConfig.Verbose)
                    {
                        string msg = string.Format("Kafka producer sent messages for topics {0} to broker {1} on {2}:{3}",
                                                   messagesPerTopic, brokerId, syncProducer.Config.Host, syncProducer.Config.Port);
                        Logger.Debug(msg);
                    }

                    if (response != null)
                    {
                        int statusCount = response.Statuses.Count();
                        //In java version
                        //https://git-wip-us.apache.org/repos/asf?p=kafka.git;a=blob;f=core/src/main/scala/kafka/producer/async/DefaultEventHandler.scala;h=821901e4f434dfd9eec6eceabfc2e1e65507a57c;hb=HEAD#l260
                        //The producerRequest.data just the messagesPerTopic.  So there compare the statusCount with producerRequest.data.size
                        //But in this C# version, the producerRequest.Data already grouped by topic.  So here need compare with messagesPerTopic.Count()
                        int requestCount = messagesPerTopic.Count();
                        if (statusCount != requestCount)
                        {
                            StringBuilder sb = new StringBuilder();
                            sb.AppendFormat("Incomplete response count {0} for producer request count {1}. ", statusCount, requestCount);
                            sb.AppendFormat(" Broker {0} on {1}:{2}", brokerId, syncProducer.Config.Host, syncProducer.Config.Port);
                            sb.Append(" Message detail:");
                            sb.Append(string.Join(",", messagesPerTopic.Select(r => string.Format("{0},{1}", r.Key.Topic, r.Key.PartitionId))));
                            sb.Append(" Response status detail which has error:");
                            sb.Append(string.Join(",", response.Statuses.Where(r => r.Value.Error != (short)ErrorMapping.NoError).Select(r => r.ToString())));
                            throw new FailedToSendMessageException <TK>(sb.ToString());
                        }
                        return(new ProducerSendResult <IEnumerable <Tuple <TopicAndPartition, ProducerResponseStatus> > >(response.Statuses.Where(s => s.Value.Error != (short)ErrorMapping.NoError)
                                                                                                                          .Select(s => new Tuple <TopicAndPartition, ProducerResponseStatus>(s.Key, s.Value))));
                    }
                }
            }
            catch (NoLeaderForPartitionException e)
            {
                Logger.Error(ExceptionUtil.GetExceptionDetailInfo(e));
                return(new ProducerSendResult <IEnumerable <Tuple <TopicAndPartition, ProducerResponseStatus> > >(messagesPerTopic.Keys.Select(
                                                                                                                      s => new Tuple <TopicAndPartition, ProducerResponseStatus>(s, new ProducerResponseStatus {
                    Error = ErrorMapping.NotLeaderForPartitionCode
                })), e));
            }
            catch (Exception e)
            {
                Logger.Error(ExceptionUtil.GetExceptionDetailInfo(e));
                return(new ProducerSendResult <IEnumerable <Tuple <TopicAndPartition, ProducerResponseStatus> > >(messagesPerTopic.Keys.Select(
                                                                                                                      s => new Tuple <TopicAndPartition, ProducerResponseStatus>(s, new ProducerResponseStatus {
                    Error = ErrorMapping.UnknownCode
                })), e));
            }

            return(new ProducerSendResult <IEnumerable <Tuple <TopicAndPartition, ProducerResponseStatus> > >(Enumerable.Empty <Tuple <TopicAndPartition, ProducerResponseStatus> >()));
        }