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  }