func (this *WatchAppError) consumePartition(zkcluster *zk.ZkCluster, consumer sarama.Consumer, topic string, partitionId int32, offset int64, msgCh chan<- *sarama.ConsumerMessage, wg *sync.WaitGroup) { defer wg.Done() p, err := consumer.ConsumePartition(topic, partitionId, offset) if err != nil { log.Error("%s %s/%d: offset=%d %v", zkcluster.Name(), topic, partitionId, offset, err) return } defer p.Close() for { select { case <-this.Stop: return case err := <-p.Errors(): log.Critical("cluster[%s] %s/%d: %s", zkcluster.Name(), topic, partitionId, err) return case msg := <-p.Messages(): if msg != nil && this.predicate(msg.Value) { msgCh <- msg } } } }
func (this *Peek) consumePartition(zkcluster *zk.ZkCluster, kfk sarama.Client, consumer sarama.Consumer, topic string, partitionId int32, msgCh chan *sarama.ConsumerMessage, offset int64) { p, err := consumer.ConsumePartition(topic, partitionId, offset) if err != nil { this.Ui.Error(fmt.Sprintf("%s %s/%d: offset=%d %v", zkcluster.Name(), topic, partitionId, offset, err)) return } defer p.Close() n := int64(0) for { select { case <-this.quit: return case msg := <-p.Messages(): msgCh <- msg n++ if this.lastN > 0 && n >= this.lastN { return } } } }
func (this *Topics) clusterSummary(zkcluster *zk.ZkCluster) []topicSummary { r := make([]topicSummary, 0, 10) kfk, err := sarama.NewClient(zkcluster.BrokerList(), saramaConfig()) if err != nil { this.Ui.Error(err.Error()) return nil } defer kfk.Close() topicInfos, _ := kfk.Topics() for _, t := range topicInfos { flat := int64(0) cum := int64(0) alivePartitions, _ := kfk.WritablePartitions(t) for _, partitionID := range alivePartitions { latestOffset, _ := kfk.GetOffset(t, partitionID, sarama.OffsetNewest) oldestOffset, _ := kfk.GetOffset(t, partitionID, sarama.OffsetOldest) flat += (latestOffset - oldestOffset) cum += latestOffset } r = append(r, topicSummary{zkcluster.ZkZone().Name(), zkcluster.Name(), t, len(alivePartitions), flat, cum}) } return r }
func (this *Top) clusterTopConsumers(zkcluster *zk.ZkCluster) { var topic string for { total := zkcluster.TotalConsumerOffsets(this.topicPattern) if this.topicPattern != "" { topic = this.topicPattern } else { topic = "-all-" } key := zkcluster.Name() + ":" + topic this.mu.Lock() this.consumerCounters[key] = float64(total) this.mu.Unlock() if !this.dashboardGraph { this.mu.Lock() this.counters[key] = float64(total) this.mu.Unlock() } time.Sleep(this.topInterval) } }
func (this *Mirror) makeMirror(c1, c2 *zk.ZkCluster) { pub, err := this.makePub(c2) swallow(err) topics, topicsChanges, err := c1.WatchTopics() swallow(err) log.Printf("topics: %+v", topics) if len(topics) == 0 { log.Println("empty topics") return } group := fmt.Sprintf("%s.%s._mirror_", c1.Name(), c2.Name()) sub, err := this.makeSub(c1, group, topics) swallow(err) pumpStopper := make(chan struct{}) go this.pump(sub, pub, pumpStopper) LOOP: for { select { case <-topicsChanges: log.Println("topics changed, stopping pump...") pumpStopper <- struct{}{} // stop pump <-pumpStopper // await pump cleanup // refresh c1 topics topics, err = c1.Topics() if err != nil { // TODO how to handle this err? log.Println(err) } log.Printf("topics: %+v", topics) sub, err = this.makeSub(c1, group, topics) if err != nil { // TODO how to handle this err? log.Println(err) } go this.pump(sub, pub, pumpStopper) case <-this.quit: log.Println("awaiting pump cleanup...") <-pumpStopper log.Printf("total transferred: %s %smsgs", gofmt.ByteSize(this.transferBytes), gofmt.Comma(this.transferN)) break LOOP } } pub.Close() }
func (this *TopBroker) clusterTopProducers(zkcluster *zk.ZkCluster) { kfk, err := sarama.NewClient(zkcluster.BrokerList(), sarama.NewConfig()) if err != nil { return } defer kfk.Close() for { hostOffsets := make(map[string]int64) topics, err := kfk.Topics() swallow(err) <-this.signalsCh[zkcluster.Name()] for _, topic := range topics { if !patternMatched(topic, this.topic) { continue } partions, err := kfk.WritablePartitions(topic) swallow(err) for _, partitionID := range partions { leader, err := kfk.Leader(topic, partitionID) swallow(err) latestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetNewest) swallow(err) host, _, err := net.SplitHostPort(leader.Addr()) swallow(err) if this.shortIp { host = shortIp(host) } if _, present := hostOffsets[host]; !present { hostOffsets[host] = 0 } hostOffsets[host] += latestOffset } } this.hostOffsetCh <- hostOffsets kfk.RefreshMetadata(topics...) } }
func (this *LsZk) printCluster(zkcluster *zk.ZkCluster) { this.Ui.Output(color.Green(zkcluster.Name())) children, err := zkcluster.ListChildren(this.recursive) if err != nil { this.Ui.Error(fmt.Sprintf("%s%s", strings.Repeat(" ", 4), err)) return } for _, c := range children { this.Ui.Output(fmt.Sprintf("%s%s", strings.Repeat(" ", 4), c)) if strings.HasSuffix(c, "brokers") { this.Ui.Output(fmt.Sprintf("%s%s/ids", strings.Repeat(" ", 4), c)) this.Ui.Output(fmt.Sprintf("%s%s/topics", strings.Repeat(" ", 4), c)) } } }
func (this *Lags) printConsumersLagTable(zkcluster *zk.ZkCluster) { lines := make([]string, 0) header := "ConsumerGroup|Topic/Partition|Produced|Consumed|Lag|Committed|Uptime" lines = append(lines, header) // sort by group name consumersByGroup := zkcluster.ConsumersByGroup(this.groupPattern) sortedGroups := make([]string, 0, len(consumersByGroup)) for group, _ := range consumersByGroup { sortedGroups = append(sortedGroups, group) } sort.Strings(sortedGroups) for _, group := range sortedGroups { if !patternMatched(group, this.groupPattern) { continue } sortedTopicAndPartitionIds := make([]string, 0, len(consumersByGroup[group])) consumers := make(map[string]zk.ConsumerMeta) for _, t := range consumersByGroup[group] { key := fmt.Sprintf("%s:%s", t.Topic, t.PartitionId) sortedTopicAndPartitionIds = append(sortedTopicAndPartitionIds, key) consumers[key] = t } sort.Strings(sortedTopicAndPartitionIds) for _, topicAndPartitionId := range sortedTopicAndPartitionIds { consumer := consumers[topicAndPartitionId] if !patternMatched(consumer.Topic, this.topicPattern) { continue } if !consumer.Online { continue } if this.problematicMode && consumer.Lag <= int64(this.lagThreshold) { continue } if consumer.ConsumerZnode == nil { this.Ui.Warn(fmt.Sprintf("%+v has no znode", consumer)) continue } lines = append(lines, fmt.Sprintf("%s|%s/%s|%s|%s|%s|%s|%s", group, consumer.Topic, consumer.PartitionId, gofmt.Comma(consumer.ProducerOffset), gofmt.Comma(consumer.ConsumerOffset), gofmt.Comma(consumer.Lag), gofmt.PrettySince(consumer.Mtime.Time()), gofmt.PrettySince(consumer.ConsumerZnode.Uptime()))) } } if len(lines) > 1 { this.Ui.Info(fmt.Sprintf("%s ▾", zkcluster.Name())) this.Ui.Output(columnize.SimpleFormat(lines)) } }
func (this *UnderReplicated) displayUnderReplicatedPartitionsOfCluster(zkcluster *zk.ZkCluster) []string { brokerList := zkcluster.BrokerList() if len(brokerList) == 0 { this.Ui.Warn(fmt.Sprintf("%s empty brokers", zkcluster.Name())) return nil } kfk, err := sarama.NewClient(brokerList, saramaConfig()) if err != nil { this.Ui.Error(fmt.Sprintf("%s %+v %s", zkcluster.Name(), brokerList, err.Error())) return nil } defer kfk.Close() topics, err := kfk.Topics() swallow(err) if len(topics) == 0 { return nil } lines := make([]string, 0, 10) for _, topic := range topics { // get partitions and check if some dead alivePartitions, err := kfk.WritablePartitions(topic) if err != nil { this.Ui.Error(fmt.Sprintf("%s topic[%s] cannot fetch writable partitions: %v", zkcluster.Name(), topic, err)) continue } partions, err := kfk.Partitions(topic) if err != nil { this.Ui.Error(fmt.Sprintf("%s topic[%s] cannot fetch partitions: %v", zkcluster.Name(), topic, err)) continue } if len(alivePartitions) != len(partions) { this.Ui.Error(fmt.Sprintf("%s topic[%s] has %s partitions: %+v/%+v", zkcluster.Name(), topic, color.Red("dead"), alivePartitions, partions)) } for _, partitionID := range alivePartitions { replicas, err := kfk.Replicas(topic, partitionID) if err != nil { this.Ui.Error(fmt.Sprintf("%s topic[%s] P:%d: %v", zkcluster.Name(), topic, partitionID, err)) continue } isr, isrMtime, partitionCtime := zkcluster.Isr(topic, partitionID) underReplicated := false if len(isr) != len(replicas) { underReplicated = true } if underReplicated { leader, err := kfk.Leader(topic, partitionID) swallow(err) latestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetNewest) swallow(err) oldestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetOldest) swallow(err) lines = append(lines, fmt.Sprintf("\t%s Partition:%d/%s Leader:%d Replicas:%+v Isr:%+v/%s Offset:%d-%d Num:%d", topic, partitionID, gofmt.PrettySince(partitionCtime), leader.ID(), replicas, isr, gofmt.PrettySince(isrMtime), oldestOffset, latestOffset, latestOffset-oldestOffset)) } } } return lines }
func (this *Mirror) runMirror(c1, c2 *zk.ZkCluster, limit int64) { this.startedAt = time.Now() log.Info("start [%s/%s] -> [%s/%s] with bandwidth %sbps", c1.ZkZone().Name(), c1.Name(), c2.ZkZone().Name(), c2.Name(), gofmt.Comma(limit*8)) pub, err := this.makePub(c2) if err != nil { panic(err) } log.Trace("pub[%s/%s] created", c2.ZkZone().Name(), c2.Name()) go func(pub sarama.AsyncProducer, c *zk.ZkCluster) { for { select { case <-this.quit: return case err := <-pub.Errors(): // messages will only be returned here after all retry attempts are exhausted. // // e,g // Failed to produce message to topic xx: write tcp src->kfk: i/o timeout // kafka: broker not connected log.Error("pub[%s/%s] %v", c.ZkZone().Name(), c.Name(), err) } } }(pub, c2) group := this.groupName(c1, c2) ever := true round := 0 for ever { round++ topics, topicsChanges, err := c1.WatchTopics() if err != nil { log.Error("#%d [%s/%s]watch topics: %v", round, c1.ZkZone().Name(), c1.Name(), err) time.Sleep(time.Second * 10) continue } topics = this.realTopics(topics) sub, err := this.makeSub(c1, group, topics) if err != nil { log.Error("#%d [%s/%s] %v", round, c1.ZkZone().Name(), c1.Name(), err) time.Sleep(time.Second * 10) continue } log.Info("#%d starting pump [%s/%s] -> [%s/%s] %d topics with group %s for %+v", round, c1.ZkZone().Name(), c1.Name(), c2.ZkZone().Name(), c2.Name(), len(topics), group, topics) pumpStopper := make(chan struct{}) pumpStopped := make(chan struct{}) go this.pump(sub, pub, pumpStopper, pumpStopped) select { case <-topicsChanges: // TODO log the diff the topics log.Warn("#%d [%s/%s] topics changed, stopping pump...", round, c1.Name(), c2.Name()) pumpStopper <- struct{}{} // stop pump <-pumpStopped // await pump cleanup case <-this.quit: log.Info("#%d awaiting pump cleanup...", round) <-pumpStopped ever = false case <-pumpStopped: // pump encounters problems, just retry } } log.Info("total transferred: %s %smsgs", gofmt.ByteSize(this.transferBytes), gofmt.Comma(this.transferN)) log.Info("closing pub...") pub.Close() }
func (this *Mirror) groupName(c1, c2 *zk.ZkCluster) string { return fmt.Sprintf("_mirror_.%s.%s.%s.%s", c1.ZkZone().Name(), c1.Name(), c2.ZkZone().Name(), c2.Name()) }
func (this *Topics) displayTopicsOfCluster(zkcluster *zk.ZkCluster) { echoBuffer := func(lines []string) { for _, l := range lines { this.Ui.Output(l) } } linesInTopicMode := make([]string, 0) if this.verbose { linesInTopicMode = this.echoOrBuffer(zkcluster.Name(), linesInTopicMode) } // get all alive brokers within this cluster brokers := zkcluster.Brokers() if len(brokers) == 0 { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%4s%s", " ", color.Red("%s empty brokers", zkcluster.Name())), linesInTopicMode) echoBuffer(linesInTopicMode) return } if this.verbose { sortedBrokerIds := make([]string, 0, len(brokers)) for brokerId, _ := range brokers { sortedBrokerIds = append(sortedBrokerIds, brokerId) } sort.Strings(sortedBrokerIds) for _, brokerId := range sortedBrokerIds { if this.ipInNumber { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%4s%s %s", " ", color.Green(brokerId), brokers[brokerId]), linesInTopicMode) } else { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%4s%s %s", " ", color.Green(brokerId), brokers[brokerId].NamedString()), linesInTopicMode) } } } kfk, err := sarama.NewClient(zkcluster.BrokerList(), saramaConfig()) if err != nil { if this.verbose { linesInTopicMode = this.echoOrBuffer(color.Yellow("%5s%+v %s", " ", zkcluster.BrokerList(), err.Error()), linesInTopicMode) } return } defer kfk.Close() topics, err := kfk.Topics() swallow(err) if len(topics) == 0 { if this.topicPattern == "" && this.verbose { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%5s%s", " ", color.Magenta("no topics")), linesInTopicMode) echoBuffer(linesInTopicMode) } return } sortedTopics := make([]string, 0, len(topics)) for _, t := range topics { sortedTopics = append(sortedTopics, t) } sort.Strings(sortedTopics) topicsCtime := zkcluster.TopicsCtime() hasTopicMatched := false for _, topic := range sortedTopics { if !patternMatched(topic, this.topicPattern) { continue } if this.since > 0 && time.Since(topicsCtime[topic]) > this.since { continue } this.topicN++ hasTopicMatched = true if this.verbose { linesInTopicMode = this.echoOrBuffer(strings.Repeat(" ", 4)+color.Cyan(topic), linesInTopicMode) } // get partitions and check if some dead alivePartitions, err := kfk.WritablePartitions(topic) swallow(err) partions, err := kfk.Partitions(topic) swallow(err) if len(alivePartitions) != len(partions) { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%30s %s %s P: %s/%+v", zkcluster.Name(), color.Cyan("%-50s", topic), color.Red("partial dead"), color.Green("%+v", alivePartitions), partions), linesInTopicMode) } replicas, err := kfk.Replicas(topic, partions[0]) if err != nil { this.Ui.Error(fmt.Sprintf("%s/%d %v", topic, partions[0], err)) } this.partitionN += len(partions) if !this.verbose { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%30s %s %3dP %dR %s", zkcluster.Name(), color.Cyan("%-50s", topic), len(partions), len(replicas), gofmt.PrettySince(topicsCtime[topic])), linesInTopicMode) continue } for _, partitionID := range alivePartitions { leader, err := kfk.Leader(topic, partitionID) swallow(err) replicas, err := kfk.Replicas(topic, partitionID) if err != nil { this.Ui.Error(fmt.Sprintf("%s/%d %v", topic, partitionID, err)) } isr, isrMtime, partitionCtime := zkcluster.Isr(topic, partitionID) isrMtimeSince := gofmt.PrettySince(isrMtime) if time.Since(isrMtime).Hours() < 24 { // ever out of sync last 24h isrMtimeSince = color.Magenta(isrMtimeSince) } underReplicated := false if len(isr) != len(replicas) { underReplicated = true } latestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetNewest) swallow(err) oldestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetOldest) swallow(err) if this.count > 0 && (latestOffset-oldestOffset) < this.count { continue } this.totalMsgs += latestOffset - oldestOffset this.totalOffsets += latestOffset if !underReplicated { linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%8d Leader:%s Replicas:%+v Isr:%+v Offset:%16s - %-16s Num:%-15s %s-%s", partitionID, color.Green("%d", leader.ID()), replicas, isr, gofmt.Comma(oldestOffset), gofmt.Comma(latestOffset), gofmt.Comma(latestOffset-oldestOffset), gofmt.PrettySince(partitionCtime), isrMtimeSince), linesInTopicMode) } else { // use red for alert linesInTopicMode = this.echoOrBuffer(fmt.Sprintf("%8d Leader:%s Replicas:%+v Isr:%s Offset:%16s - %-16s Num:%-15s %s-%s", partitionID, color.Green("%d", leader.ID()), replicas, color.Red("%+v", isr), gofmt.Comma(oldestOffset), gofmt.Comma(latestOffset), gofmt.Comma(latestOffset-oldestOffset), gofmt.PrettySince(partitionCtime), isrMtimeSince), linesInTopicMode) } } } if this.topicPattern != "" { if hasTopicMatched { echoBuffer(linesInTopicMode) } } else { echoBuffer(linesInTopicMode) } }
func (this *Top) clusterTopProducers(zkcluster *zk.ZkCluster) { cluster := zkcluster.Name() brokerList := zkcluster.BrokerList() if len(brokerList) == 0 { return } kfk, err := sarama.NewClient(brokerList, sarama.NewConfig()) if err != nil { return } defer kfk.Close() for { topics, err := kfk.Topics() if err != nil || len(topics) == 0 { return } for _, topic := range topics { if !patternMatched(topic, this.topicPattern) { continue } msgs := int64(0) alivePartitions, err := kfk.WritablePartitions(topic) swallow(err) for _, partitionID := range alivePartitions { latestOffset, err := kfk.GetOffset(topic, partitionID, sarama.OffsetNewest) if err != nil { // this broker is down continue } msgs += latestOffset } this.mu.Lock() if _, present := this.brokers[cluster+":"+topic]; !present { // calculate the broker leading partitions leadingPartitions := make(map[string]int) // broker:lead partitions n for _, pid := range alivePartitions { leader, err := kfk.Leader(topic, pid) swallow(err) leadingPartitions[leader.Addr()]++ } brokers := make([]string, 0) for addr, n := range leadingPartitions { brokers = append(brokers, fmt.Sprintf("%d@%s", n, addr)) } this.brokers[cluster+":"+topic] = this.discardPortOfBrokerAddr(brokers) } this.counters[cluster+":"+topic] = float64(msgs) this.partitions[cluster+":"+topic] = len(alivePartitions) this.mu.Unlock() } time.Sleep(time.Second) kfk.RefreshMetadata(topics...) } }