github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/metrics/sinks/kafka/driver.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kafka 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "net/url" 21 "sync" 22 "time" 23 24 "github.com/golang/glog" 25 "github.com/optiopay/kafka" 26 "github.com/optiopay/kafka/proto" 27 "k8s.io/heapster/metrics/core" 28 ) 29 30 const ( 31 partition = 0 32 brokerClientID = "kafka-sink" 33 brokerDialTimeout = 10 * time.Second 34 brokerDialRetryLimit = 1 35 brokerDialRetryWait = 0 36 brokerAllowTopicCreation = true 37 brokerLeaderRetryLimit = 1 38 brokerLeaderRetryWait = 0 39 dataTopic = "heapster-metrics" 40 ) 41 42 type KafkaSinkPoint struct { 43 MetricsName string 44 MetricsValue interface{} 45 MetricsTimestamp time.Time 46 MetricsTags map[string]string 47 } 48 49 type kafkaSink struct { 50 producer kafka.Producer 51 dataTopic string 52 sync.RWMutex 53 } 54 55 func (sink *kafkaSink) ExportData(dataBatch *core.DataBatch) { 56 sink.Lock() 57 defer sink.Unlock() 58 59 for _, metricSet := range dataBatch.MetricSets { 60 for metricName, metricValue := range metricSet.MetricValues { 61 point := KafkaSinkPoint{ 62 MetricsName: metricName, 63 MetricsTags: metricSet.Labels, 64 MetricsValue: map[string]interface{}{ 65 "value": metricValue.GetValue(), 66 }, 67 MetricsTimestamp: dataBatch.Timestamp.UTC(), 68 } 69 sink.produceKafkaMessage(point, sink.dataTopic) 70 } 71 for _, metric := range metricSet.LabeledMetrics { 72 labels := make(map[string]string) 73 for k, v := range metricSet.Labels { 74 labels[k] = v 75 } 76 for k, v := range metric.Labels { 77 labels[k] = v 78 } 79 point := KafkaSinkPoint{ 80 MetricsName: metric.Name, 81 MetricsTags: labels, 82 MetricsValue: map[string]interface{}{ 83 "value": metric.GetValue(), 84 }, 85 MetricsTimestamp: dataBatch.Timestamp.UTC(), 86 } 87 sink.produceKafkaMessage(point, sink.dataTopic) 88 } 89 } 90 } 91 92 func (sink *kafkaSink) produceKafkaMessage(dataPoint KafkaSinkPoint, topic string) error { 93 start := time.Now() 94 jsonItems, err := json.Marshal(dataPoint) 95 if err != nil { 96 return fmt.Errorf("failed to transform the items to json : %s", err) 97 } 98 message := &proto.Message{Value: []byte(string(jsonItems))} 99 _, err = sink.producer.Produce(topic, partition, message) 100 if err != nil { 101 return fmt.Errorf("failed to produce message to %s:%d: %s", topic, partition, err) 102 } 103 end := time.Now() 104 glog.V(4).Info("Exported %d data to kafka in %s", len([]byte(string(jsonItems))), end.Sub(start)) 105 return nil 106 } 107 108 func (sink *kafkaSink) Name() string { 109 return "Apache Kafka Sink" 110 } 111 112 func (sink *kafkaSink) Stop() { 113 // nothing needs to be done. 114 } 115 116 // setupProducer returns a producer of kafka server 117 func setupProducer(sinkBrokerHosts []string, brokerConf kafka.BrokerConf) (kafka.Producer, error) { 118 glog.V(3).Infof("attempting to setup kafka sink") 119 broker, err := kafka.Dial(sinkBrokerHosts, brokerConf) 120 if err != nil { 121 return nil, fmt.Errorf("failed to connect to kafka cluster: %s", err) 122 } 123 defer broker.Close() 124 125 //create kafka producer 126 conf := kafka.NewProducerConf() 127 conf.RequiredAcks = proto.RequiredAcksLocal 128 sinkProducer := broker.Producer(conf) 129 glog.V(3).Infof("kafka sink setup successfully") 130 return sinkProducer, nil 131 } 132 133 func NewKafkaSink(uri *url.URL) (core.DataSink, error) { 134 opts, err := url.ParseQuery(uri.RawQuery) 135 if err != nil { 136 return nil, fmt.Errorf("failed to parser url's query string: %s", err) 137 } 138 139 var topic string = dataTopic 140 if len(opts["timeseriestopic"]) > 0 { 141 topic = opts["timeseriestopic"][0] 142 } 143 144 var kafkaBrokers []string 145 if len(opts["brokers"]) < 1 { 146 return nil, fmt.Errorf("There is no broker assigned for connecting kafka") 147 } 148 kafkaBrokers = append(kafkaBrokers, opts["brokers"]...) 149 glog.V(2).Infof("initializing kafka sink with brokers - %v", kafkaBrokers) 150 151 //structure the config of broker 152 brokerConf := kafka.NewBrokerConf(brokerClientID) 153 brokerConf.DialTimeout = brokerDialTimeout 154 brokerConf.DialRetryLimit = brokerDialRetryLimit 155 brokerConf.DialRetryWait = brokerDialRetryWait 156 brokerConf.LeaderRetryLimit = brokerLeaderRetryLimit 157 brokerConf.LeaderRetryWait = brokerLeaderRetryWait 158 brokerConf.AllowTopicCreation = true 159 160 // set up producer of kafka server. 161 sinkProducer, err := setupProducer(kafkaBrokers, brokerConf) 162 if err != nil { 163 return nil, fmt.Errorf("Failed to setup Producer: - %v", err) 164 } 165 166 return &kafkaSink{ 167 producer: sinkProducer, 168 dataTopic: topic, 169 }, nil 170 }