Go operates Kafka with sarama

679 阅读3分钟

why is kafka

Apache Kafka 是一种实时数据流技术,每天能够处理数万亿个事件。Kafka 最初被设想为消息队列,基于分布式提交日志的抽象。自 2011 年创建并开源以来,Kafka 已成为处理动态数据的行业标准

sarama

这里我们使用在go当中常用的第三方插件 Go语言中连接kafka使用第三方库

1、下载及安装`

go get github.com/Shopify/sarama

注意

sarama v1.20之后的版本加入了zstd压缩算法,需要用到cgo,在Windows平台编译时会提示类似如下错误:

# github.com/DataDog/zstd
exec: "gcc":executable file not found in %PATH%

所以在Windows平台请使用v1.19版本的sarama。

2、连接kafka发送消息

package main

import (
	"fmt"

	"github.com/Shopify/sarama"
)

// 基于sarama第三方库开发的kafka client

func main() {
	config := sarama.NewConfig()
	config.Producer.RequiredAcks = sarama.WaitForAll          // 发送完数据需要leader和follow都确认
	config.Producer.Partitioner = sarama.NewRandomPartitioner // 新选出一个partition
	config.Producer.Return.Successes = true                   // 成功交付的消息将在success channel返回

	// 构造一个消息
	msg := &sarama.ProducerMessage{}
	msg.Topic = "web_log"
	msg.Value = sarama.StringEncoder("this is a test log")
	// 连接kafka
	client, err := sarama.NewSyncProducer([]string{"127.0.0.1:9092"}, config)
	if err != nil {
		fmt.Println("producer closed, err:", err)
		return
	}
	defer client.Close()
	// 发送消息
	pid, offset, err := client.SendMessage(msg)
	if err != nil {
		fmt.Println("send msg failed, err:", err)
		return
	}
	fmt.Printf("pid:%v offset:%v\n", pid, offset)
}

3、连接kafka消费消息

package main

import (
	"fmt"

	"github.com/Shopify/sarama"
)

// kafka consumer
func main() {
	consumer, err := sarama.NewConsumer([]string{"127.0.0.1:9092"}, nil)
	if err != nil {
		fmt.Printf("fail to start consumer, err:%v\n", err)
		return
	}
	partitionList, err := consumer.Partitions("web_log") // 根据topic取到所有的分区
	if err != nil {
		fmt.Printf("fail to get list of partition:err%v\n", err)
		return
	}
	fmt.Println(partitionList)
	for partition := range partitionList { // 遍历所有的分区
		// 针对每个分区创建一个对应的分区消费者
		pc, err := consumer.ConsumePartition("web_log", int32(partition), sarama.OffsetNewest)
		if err != nil {
			fmt.Printf("failed to start consumer for partition %d,err:%v\n", partition, err)
			return
		}
		defer pc.AsyncClose()
		// 异步从每个分区消费信息
		go func(sarama.PartitionConsumer) {
                      wg.Add(1)//等待数量+1
			for msg := range pc.Messages() {
				fmt.Printf("Partition:%d Offset:%d Key:%v Value:%v", msg.Partition, msg.Offset, msg.Key, msg.Value)
			}
                        wg.Done()//等待数量-1
		}(pc)
	}
	time.Sleep(time.Hour)
}

4、至此一个简单的kafka生产者和消费者就完成了,具体的业务可再次基础之上封装抽象

随着go语音开发大军的增长sarama也出现了一些不一样声音

为什么不推荐使用Sarama Go客户端收发消息?

问题现象

所有Sarama Go版本客户端存在以下已知问题:

  • 当Topic新增分区时,Sarama Go客户端无法感知并消费新增分区,需要客户端重启后,才能消费到新增分区。
  • 当Sarama Go客户端同时订阅两个以上的Topic时,有可能会导致部分分区无法正常消费消息。
  • 当Sarama Go客户端的消费位点重置策略设置为Oldest(earliest)时,如果客户端宕机或服务端版本升级,由于Sarama Go客户端自行实现OutOfRange机制,有可能会导致客户端从最小位点开始重新消费所有消息。

解决方案

Confluent Go请访问github.com/confluentin…

注意 如果无法在短期内替换客户端,请注意以下事项:

  • 针对生产环境,请将位点重置策略设置为Newest(latest);针对测试环境,或者其他明确可以接收大量重复消息的场景,设置为Oldest(earliest)
  • 如果发生了位点重置,产生大量堆积,您可以使用消息队列Kafka版控制台提供的重置消费位点功能,手动重置消费位点到某一时间点,无需改代码或换Consumer Group。具体操作,