Ejemplo n.º 1
0
func (ks *kafkaSink) setupClient() error {
	glog.V(3).Infof("attempting to setup kafka sink")
	broker, err := kafka.Dial(ks.sinkBrokerHosts, ks.brokerConf)
	if err != nil {
		return fmt.Errorf("failed to connect to kafka cluster: %s", err)
	}
	defer broker.Close()
	//create kafka producer
	conf := kafka.NewProducerConf()
	conf.RequiredAcks = proto.RequiredAcksLocal
	sinkProducer := broker.Producer(conf)
	ks.producer = sinkProducer
	glog.V(3).Infof("kafka sink setup successfully")
	return nil
}
Ejemplo n.º 2
0
// setupProducer returns a producer of kafka server
func setupProducer(sinkBrokerHosts []string, brokerConf kafka.BrokerConf) (kafka.Producer, error) {
	glog.V(3).Infof("attempting to setup kafka sink")
	broker, err := kafka.Dial(sinkBrokerHosts, brokerConf)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to kafka cluster: %s", err)
	}
	defer broker.Close()

	//create kafka producer
	conf := kafka.NewProducerConf()
	conf.RequiredAcks = proto.RequiredAcksLocal
	sinkProducer := broker.Producer(conf)
	glog.V(3).Infof("kafka sink setup successfully")
	return sinkProducer, nil
}
Ejemplo n.º 3
0
func TestNewTopic(t *testing.T) {
	IntegrationTest(t)
	const msgPerTopic = 10

	topic := "NewTopic"

	cluster := NewKafkaCluster("kafka-docker/", 1)
	if err := cluster.Start(); err != nil {
		t.Fatalf("cannot start kafka cluster: %s", err)
	}
	defer func() {
		_ = cluster.Stop()
	}()

	bconf := kafka.NewBrokerConf("producer-new-topic")
	bconf.Logger = &testLogger{t}
	bconf.AllowTopicCreation = true
	addrs, err := cluster.KafkaAddrs()
	if err != nil {
		t.Fatalf("cannot get kafka address: %s", err)
	}
	broker, err := kafka.Dial(addrs, bconf)
	if err != nil {
		t.Fatalf("cannot connect to cluster (%q): %s", addrs, err)
	}
	defer broker.Close()

	m := proto.Message{
		Value: []byte("Test message"),
	}
	pconf := kafka.NewProducerConf()
	producer := broker.Producer(pconf)

	if _, err := producer.Produce(topic, 0, &m); err != nil {
		t.Fatalf("cannot produce to %q: %s", topic, err)
	}

	consumer, err := broker.Consumer(kafka.NewConsumerConf(topic, 0))
	if err != nil {
		t.Fatalf("cannot create consumer for %q: %s", topic, err)
	}
	if _, err := consumer.Consume(); err != nil {
		t.Errorf("cannot consume message from %q: %s", topic, err)
	}
}
Ejemplo n.º 4
0
func (w *writer) ensureInitialized() error {
	return w.retry.Do(func() (err error) {
		w.broker, err = kafka.Dial(w.endpoints, kafka.NewBrokerConf(w.clientId))
		if err != nil {
			return
		}
		var count int32
		count, err = w.broker.PartitionCount(w.topic)
		if err != nil {
			return
		}
		conf := kafka.NewProducerConf()
		conf.RequiredAcks = proto.RequiredAcksLocal
		producer := w.broker.Producer(conf)
		w.producer = newChannelProducer(producer, count)
		return
	})
}
Ejemplo n.º 5
0
// produceStdin read stdin and send every non empty line as message
func produceStdin(broker kafka.Client) {
	producer := broker.Producer(kafka.NewProducerConf())
	input := bufio.NewReader(os.Stdin)
	for {
		line, err := input.ReadString('\n')
		if err != nil {
			log.Fatalf("input error: %s", err)
		}
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		}

		msg := &proto.Message{Value: []byte(line)}
		if _, err := producer.Produce(topic, partition, msg); err != nil {
			log.Fatalf("cannot produce message to %s:%d: %s", topic, partition, err)
		}
	}
}
Ejemplo n.º 6
0
func ExampleBroker_Producer() {
	broker := NewBroker()
	msg := &proto.Message{Value: []byte("first")}

	producer := broker.Producer(kafka.NewProducerConf())

	// mock server actions, handling any produce call
	go func() {
		resp, err := broker.ReadProducers(time.Millisecond * 20)
		if err != nil {
			panic(fmt.Sprintf("failed reading producers: %s", err))
		}
		if len(resp.Messages) != 1 {
			panic("expected single message")
		}
		if !reflect.DeepEqual(resp.Messages[0], msg) {
			panic("expected different message")
		}
	}()

	// provide data for above goroutine
	_, err := producer.Produce("my-topic", 0, msg)
	if err != nil {
		panic(fmt.Sprintf("cannot produce message: %s", err))
	}

	mockProducer := producer.(*Producer)

	// test error handling by forcing producer to return error,
	//
	// it is possible to manipulate produce result by changing producer's
	// ResponseOffset and ResponseError attributes
	mockProducer.ResponseError = errors.New("my spoon is too big!")
	_, err = producer.Produce("my-topic", 0, msg)
	fmt.Printf("Error: %s\n", err)

	// output:
	//
	// Error: my spoon is too big!
}
Ejemplo n.º 7
0
// setupProducer returns a producer of kafka server
func setupProducer(sinkBrokerHosts []string, topic string, brokerConf kafka.BrokerConf) (kafka.DistributingProducer, error) {
	glog.V(3).Infof("attempting to setup kafka sink")
	broker, err := kafka.Dial(sinkBrokerHosts, brokerConf)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to kafka cluster: %s", err)
	}
	defer broker.Close()

	//create kafka producer
	conf := kafka.NewProducerConf()
	conf.RequiredAcks = proto.RequiredAcksLocal
	producer := broker.Producer(conf)

	// create RoundRobinProducer with the default producer.
	count, err := broker.PartitionCount(topic)
	if err != nil {
		count = 1
		glog.Warningf("Failed to get partition count of topic %q: %s", topic, err)
	}
	sinkProducer := kafka.NewRoundRobinProducer(producer, count)
	glog.V(3).Infof("kafka sink setup successfully")
	return sinkProducer, nil
}
Ejemplo n.º 8
0
func TestConsumerBrokenConnection(t *testing.T) {
	IntegrationTest(t)
	const msgPerTopic = 10

	topics := []string{"Topic3", "Topic4"}

	cluster := NewKafkaCluster("kafka-docker/", 4)
	if err := cluster.Start(); err != nil {
		t.Fatalf("cannot start kafka cluster: %s", err)
	}
	defer func() {
		_ = cluster.Stop()
	}()

	bconf := kafka.NewBrokerConf("producer-broken-connection")
	bconf.Logger = &testLogger{t}
	addrs, err := cluster.KafkaAddrs()
	if err != nil {
		t.Fatalf("cannot get kafka address: %s", err)
	}
	broker, err := kafka.Dial(addrs, bconf)
	if err != nil {
		t.Fatalf("cannot connect to cluster (%q): %s", addrs, err)
	}
	defer broker.Close()

	// produce big message to enforce TCP buffer flush
	m := proto.Message{
		Value: []byte(strings.Repeat("consumer broken connection message ", 1000)),
	}
	pconf := kafka.NewProducerConf()
	producer := broker.Producer(pconf)

	// send message to all topics
	for _, name := range topics {
		for i := 0; i < msgPerTopic; i++ {
			if _, err := producer.Produce(name, 0, &m); err != nil {
				t.Fatalf("cannot produce to %q: %s", name, err)
			}
		}
	}

	// close two kafka clusters and publish to all 3 topics - 2 of them should
	// retry sending, because lack of leader makes the request fail
	//
	// request should not succeed until nodes are back - bring them back after
	// small delay and make sure producing was successful
	containers, err := cluster.Containers()
	if err != nil {
		t.Fatalf("cannot get containers: %s", err)
	}
	var stopped []*Container
	for _, container := range containers {
		if container.RunningKafka() {
			if err := container.Kill(); err != nil {
				t.Fatalf("cannot kill %q kafka container: %s", container.ID, err)
			}
			stopped = append(stopped, container)
		}
		if len(stopped) == 2 {
			break
		}
	}

	// bring stopped containers back
	errc := make(chan error)
	go func() {
		time.Sleep(500 * time.Millisecond)
		for _, container := range stopped {
			if err := container.Start(); err != nil {
				errc <- err
			}
		}
		close(errc)
	}()

	// make sure data was persisted
	for _, name := range topics {
		consumer, err := broker.Consumer(kafka.NewConsumerConf(name, 0))
		if err != nil {
			t.Errorf("cannot create consumer for %q: %s", name, err)
			continue
		}
		for i := 0; i < msgPerTopic; i++ {
			if _, err := consumer.Consume(); err != nil {
				t.Errorf("cannot consume %d message from %q: %s", i, name, err)
			}
		}
	}

	for err := range errc {
		t.Errorf("cannot start container: %s", err)
	}
}
Ejemplo n.º 9
0
func (self *KafkaOutput) Init(pcf *plugins.PluginCommonConfig, conf toml.Primitive) (err error) {
	log.Println("KafkaOutput Init.")
	self.common = pcf
	hn, err := os.Hostname()
	if err != nil {
		hn = "kamanclient"
	}
	self.config = &KafkaOutputConfig{
		ClientId:      hn,
		Distributer:   "None",
		Partitions:    0,
		FlushInterval: 1000,
	}
	if err = toml.PrimitiveDecode(conf, self.config); err != nil {
		return fmt.Errorf("Can't unmarshal KafkaOutput config: %s", err)
	}
	if len(self.config.Addrs) == 0 {
		return errors.New("addrs must have at least one entry")
	}
	if len(self.config.Topic) == 0 {
		return fmt.Errorf("topic is empty")
	}

	bcf := kafka.NewBrokerConf(self.config.ClientId)
	//bcf.AllowTopicCreation = true

	// connect to kafka cluster
	self.broker, err = kafka.Dial(self.config.Addrs, bcf)
	if err != nil {
		return fmt.Errorf("cannot connect to kafka cluster: %s", err)
	}

	defer self.broker.Close()
	pf := kafka.NewProducerConf()
	pf.RequiredAcks = 1
	self.producer = self.broker.Producer(pf)
	partitions, err := self.broker.PartitionCount(self.config.Topic)
	if err != nil {
		return fmt.Errorf("cannot count to topic partitions: %s", err)
	}
	log.Printf("topic\"%s\" has %d partitions\n", self.config.Topic, partitions)
	if (self.config.Partition + 1) > partitions {
		return fmt.Errorf("invalid partition: %d, topic have %d partitions",
			self.config.Partition, partitions)
	}
	if self.config.Partitions == 0 {
		self.config.Partitions = partitions
	}
	switch self.config.Distributer {
	case "Random":
		self.distributingProducer = kafka.NewRandomProducer(self.producer, self.config.Partitions)
	case "RoundRobin":
		self.distributingProducer = kafka.NewRoundRobinProducer(self.producer, self.config.Partitions)
	case "Hash":
		self.distributingProducer = kafka.NewHashProducer(self.producer, self.config.Partitions)
	case "None":
		self.distributingProducer = nil
	default:
		return fmt.Errorf("invalid distributer: %s, must be one of these: \"Random\",\"RoundRobin\",\"Hash\"", self.config.Distributer)
	}
	self.batchChan = make(chan *outBatch)
	self.backChan = make(chan *outBatch, 2) // Never block on the hand-back
	return err
}
Ejemplo n.º 10
0
// NewMarshaler connects to a cluster (given broker addresses) and prepares to handle marshalling
// requests. Given the way this system works, the marshaler has to process all messages in the
// topic before it's safely able to begin operating. This might take a while.
func NewMarshaler(clientID, groupID string, brokers []string) (*Marshaler, error) {
	// TODO: It might be nice to make the marshaler agnostic of clients and able to support
	// requests from N clients/groups. For now, though, we require instantiating a new
	// marshaler for every client/group.
	brokerConf := kafka.NewBrokerConf("PortalMarshal")
	broker, err := kafka.Dial(brokers, brokerConf)
	if err != nil {
		return nil, err
	}

	// Get offset coordinator so we can look up (and save) committed offsets later.
	coordinatorConf := kafka.NewOffsetCoordinatorConf(groupID)
	coordinator, err := broker.OffsetCoordinator(coordinatorConf)
	if err != nil {
		return nil, err
	}

	ws := &Marshaler{
		quit:       new(int32),
		rsteps:     new(int32),
		instanceID: newInstanceID(),
		clientID:   clientID,
		groupID:    groupID,
		kafka:      broker,
		offsets:    coordinator,
		producer:   broker.Producer(kafka.NewProducerConf()),
		topics:     make(map[string]int),
		groups:     make(map[string]map[string]*topicState),
		jitters:    make(chan time.Duration, 100),
	}

	// Do an initial metadata fetch, this will block a bit
	err = ws.refreshMetadata()
	if err != nil {
		return nil, fmt.Errorf("Failed to get metadata: %s", err)
	}

	// If there is no marshal topic, then we can't run. The admins must go create the topic
	// before they can use this library. Please see the README.
	ws.partitions = ws.Partitions(MarshalTopic)
	if ws.partitions == 0 {
		return nil, errors.New("Marshalling topic not found. Please see the documentation.")
	}

	// Now we start a goroutine to start consuming each of the partitions in the marshal
	// topic. Note that this doesn't handle increasing the partition count on that topic
	// without stopping all consumers.
	ws.rationalizers.Add(ws.partitions)
	for id := 0; id < ws.partitions; id++ {
		go ws.rationalize(id, ws.kafkaConsumerChannel(id))
	}

	// A jitter calculator, just fills a channel with random numbers so that other
	// people don't have to build their own random generator...
	go func() {
		rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
		for {
			jitter := rnd.Intn(HeartbeatInterval/2) + (HeartbeatInterval / 2)
			ws.jitters <- time.Duration(jitter) * time.Second
		}
	}()

	// Now start the metadata refreshing goroutine
	go func() {
		for atomic.LoadInt32(ws.quit) != 1 {
			time.Sleep(<-ws.jitters)
			log.Infof("Refreshing topic metadata.")
			ws.refreshMetadata()

			// See if the number of partitions in the marshal topic went up. If so, this is a
			// fatal error as it means we lose coordination. In theory a mass die-off of workers
			// is bad, but so is upsharding the coordination topic without shutting down
			// everything. At least this limits the damage horizon?
			if ws.Partitions(MarshalTopic) != ws.partitions {
				log.Fatalf("Marshal topic partition count changed. FATAL!")
			}
		}
	}()

	// Wait for all rationalizers to come alive
	log.Infof("Waiting for all rationalizers to come alive.")
	ws.rationalizers.Wait()
	log.Infof("All rationalizers alive, Marshaler now alive.")

	return ws, nil
}