Kafka Golang集成

使用 sarama 连接kafka

go get -u github.com/Shopify/sarama

1. Topic

// 客户端
client, err := sarama.NewClient(brokers, config)

// 管理员
clusterAdmin, err := sarama.NewClusterAdminFromClient(client)

// topic 新增
err = clusterAdmin.CreateTopic(topicName, topicDetail, true)

// topic 列表
topicDetails, err := clusterAdmin.ListTopics()

// topic 详情
metadatas, err := clusterAdmin.DescribeTopics([]string{"goose", "test"})

// 删除 topic
err = clusterAdmin.DeleteTopic("test")

// 获取 topic 所有的分区
ids, err := client.Partions(topicName)

// 获取 topic 某个 partition 的偏移量
maxOffset, err := client.GetOffset(topicName, partitionID, sarama.OffsetNewest)

示例:

func main() {
	config := sarama.NewConfig()
	config.Version = sarama.V3_0_0_0

	brokers := []string{"192.168.80.240:30090", "192.168.80.241:30091", "192.168.80.242:30092"}

	// 客户端
	client, err := sarama.NewClient(brokers, config)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// 管理员
	clusterAdmin, err := sarama.NewClusterAdminFromClient(client)
	if err != nil {
		log.Fatal(err)
	}

	topicName := "nginx-log"
	topicDetail := &sarama.TopicDetail{
		NumPartitions:     5,
		ReplicationFactor: 3,
	}

	// topic 新增
	err = clusterAdmin.CreateTopic(topicName, topicDetail, true)
	if err != nil {
		log.Fatal(err)
	}

	// topic 列表
	topicDetails, err := clusterAdmin.ListTopics()
	if err != nil {
		log.Fatal(err)
	}
	for _, topicDetail := range topicDetails {
		log.Println(topicDetail.NumPartitions)
	}

	// topic 详情
	metadatas, err := clusterAdmin.DescribeTopics([]string{"goose", "test"})
	if err != nil {
		log.Fatal(err)
	}
	for _, metadata := range metadatas {
		log.Println(metadata.Name, len(metadata.Partitions))
	}

	// 删除 topic
	err = clusterAdmin.DeleteTopic("test")
	if err != nil {
		log.Fatal(err)
	}
}

2. Producer

2.1 Sync

producer, err := sarama.NewSyncProducerFromClient(client)

msg := &sarama.ProducerMessage{
	Topic: "nginx-log",
	Value: sarama.StringEncoder(text),
}

pid, offset, err := producer.SendMessage(msg)

示例:

func main() {
	config := sarama.NewConfig()
	config.Version = sarama.V3_0_0_0
	config.Producer.RequiredAcks = sarama.WaitForAll
	config.Producer.Partitioner = sarama.NewRandomPartitioner
	config.Producer.Return.Successes = true
	config.Consumer.Return.Errors = true

	brokers := []string{"192.168.80.240:30090", "192.168.80.241:30091", "192.168.80.242:30092"}

	client, err := sarama.NewClient(brokers, config)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	producer, err := sarama.NewSyncProducerFromClient(client)
	if err != nil {
		log.Fatal(err)
	}
	defer producer.Close()

	for i := 0; i < 10; i++ {
		text := fmt.Sprintf("kafka message %d", i+1)

		msg := &sarama.ProducerMessage{
			Topic: "nginx-log",
			Value: sarama.StringEncoder(text),
		}

		pid, offset, err := producer.SendMessage(msg)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("pid=%v, offset=%v", pid, offset)

		time.Sleep(2 * time.Second)
	}
}

2.2 Async

producer, err := sarama.NewAsyncProducerFromClient(client)

producer.Input() <- &sarama.ProducerMessage{Topic: "nginx-log", Value: sarama.StringEncoder("hello world")}

示例:

func main() {
	config := sarama.NewConfig()
	config.Version = sarama.V3_0_0_0
	config.Producer.RequiredAcks = sarama.WaitForLocal                      // Only wait for the leader to ack
	config.Producer.Compression = sarama.CompressionSnappy                  // Compress messages
	config.Producer.Flush.Frequency = time.Duration(500) * time.Millisecond // Flush batches every 500ms
	config.Producer.Partitioner = sarama.NewRandomPartitioner
	config.Producer.Return.Successes = true
	config.Consumer.Return.Errors = true

	brokers := []string{"192.168.80.240:30090", "192.168.80.241:30091", "192.168.80.242:30092"}

	client, err := sarama.NewClient(brokers, config)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	producer, err := sarama.NewAsyncProducerFromClient(client)
	if err != nil {
		log.Fatal(err)
	}
	defer producer.Close()

	for i := 0; i < 10; i++ {
		text := fmt.Sprintf("kafka async message %d", i+1)

		msg := &sarama.ProducerMessage{
			Topic:     "nginx-log",
			Value:     sarama.StringEncoder(text),
			Timestamp: time.Now(),
		}

		producer.Input() <- msg
	}

	go func() {
	LOOP:
		for {
			select {
			case success := <-producer.Successes():
				log.Printf("Topic: %s, Partition: %d, Value: %s, Offset: %d, timestamp: %v\n",
					success.Topic, success.Partition, success.Value, success.Offset, success.Timestamp)
			case failure := <-producer.Errors():
				log.Printf("error: %v\n", failure.Error())
				break LOOP
			default:
				log.Println("waiting kafka server response")
				time.Sleep(time.Second)
			}
		}
	}()

	sigterm := make(chan os.Signal, 1)
	signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
	<-sigterm
}

3. Consumer

consumer, err := sarama.NewConsumerFromClient(client)

partitionList, err := consumer.Partitions("nginx-log")

// 按偏移量查询消息
partitionConsumer, err := consumer.ConsumePartition("nginx-log", partition, sarama.OffsetOldest)
for msg := range partitionConsumer.Messages() {
	log.Println(msg.Topic, msg.Value, msg.Offset)
}

// 按时间查询消息
startOffset, err := client.GetOffset(topic, partition, startTime)
messages, err := consumer.ConsumePartition(topic, partition, startOffset)

// 获取某topic某partition某offset的消费消息;当消息已消费,此处报错。
messages, err := sarama.Consumer.ConsumePartition(topic, partition, offset)

// 通过 topicName和PartitionID 获取偏移量
nextOffset, _, err := offsetClient.GetPartionOffset(topicName,PartitionID)

// 重置offset
partitionManager, err := offsetManager.ManagePartition(topic,partition)
partitionManager.ResetOffset(offset, metadata)

示例:

func main() {
	config := sarama.NewConfig()
	config.Version = sarama.V3_0_0_0
	config.Consumer.Return.Errors = true

	brokers := []string{"192.168.80.240:30090", "192.168.80.241:30091", "192.168.80.242:30092"}

	// 客户端
	client, err := sarama.NewClient(brokers, config)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	consumer, err := sarama.NewConsumerFromClient(client)
	if err != nil {
		log.Fatal(err)
	}

	var wg sync.WaitGroup

	partitionList, err := consumer.Partitions("nginx-log")
	if err != nil {
		log.Fatal(err)
	}

	for _, partition := range partitionList {
		partitionConsumer, err := consumer.ConsumePartition("nginx-log", partition, sarama.OffsetNewest)
		if err != nil {
			log.Fatal(err)
		}

		wg.Add(1)
		go func(pc sarama.PartitionConsumer) {
			defer wg.Done()
			defer pc.AsyncClose()

			for msg := range pc.Messages() {
				fmt.Printf("Topic:%s, Partition:%d, Offset:%d, Key:%s, Value:%s\n", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value)
			}
		}(partitionConsumer)
	}

	wg.Wait()
	consumer.Close()
}

4. Consumer Group

// 消费组偏移量管理
offsetManager, err := sarama.NewOffsetManagerFromClient(group, client)

// 获取消费者组列表
groupMap, err := clusterAdmin.ListConsumerGroup()

// 消费组详情
descriptions, err := clusterAdmin.DescribeConsumerGroups(groups)

// 创建消费组查询消息
saramaMsgs, err := consumerGroup.Consume(ctx, topics, &consumer)

// 实现 ConsumerGroupHandler 接口
type Consumer struct {
	ready chan bool
}

func (c *Consumer) Setup(sarama.ConsumerGroupSession) error {
	close(c.ready)
	return nil
}

func (c *Consumer) Cleanup(sarama.ConsumerGroupSession) error {
	return nil
}

func (c *Consumer) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
	for message := range claim.Messages() {
		log.Printf("Message claimed: value = %s, timestamp = %v, topic = %s", string(message.Value), message.Timestamp, message.Topic)
		session.MarkMessage(message, "")
	}
	return nil
}

示例:

func main() {
	config := sarama.NewConfig()
	config.Version = sarama.V3_0_0_0
	config.Consumer.Group.Rebalance.Strategy = sarama.BalanceStrategyRoundRobin

	brokers := []string{"192.168.80.240:30090", "192.168.80.241:30091", "192.168.80.242:30092"}

	// 客户端
	client, err := sarama.NewClient(brokers, config)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// 消费者组
	ctx, cancel := context.WithCancel(context.Background())
	topics := []string{"nginx-log"}
	consumer := &NginxLogConsumer{
		ready: make(chan bool),
	}
	consumerGroup, _ := sarama.NewConsumerGroupFromClient("group-1", client)
	defer consumerGroup.Close()

	wg := &sync.WaitGroup{}
	wg.Add(1)

	go func() {
		defer wg.Done()
		for {
			if err = consumerGroup.Consume(ctx, topics, consumer); err != nil {
				log.Fatal(err)
			}

			if ctx.Err() != nil {
				return
			}

			consumer.ready = make(chan bool)
		}
	}()

	<-consumer.ready

	sigterm := make(chan os.Signal, 1)
	signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM)
	select {
	case <-sigterm:
		log.Println("terminating: via signal")
	case <-ctx.Done():
		log.Println("terminating: context cancelled")
	}

	cancel()
	wg.Wait()
}

type NginxLogConsumer struct {
	ready chan bool
}

func (c *NginxLogConsumer) Setup(session sarama.ConsumerGroupSession) error {
	close(c.ready)
	return nil
}

func (c *NginxLogConsumer) Cleanup(session sarama.ConsumerGroupSession) error {
	return nil
}

func (c *NginxLogConsumer) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
	for msg := range claim.Messages() {
		fmt.Printf("Topic:%s, Partition:%d, Offset:%d, Key:%s, Value:%s\n", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value)
		session.MarkMessage(msg, "")

		time.Sleep(time.Second)
	}
	return nil
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值