1.简介
Kafka 有 5 个核心 API:
在GO sarama 中只展示了
Kafka分为同步生产者和异步生产者,顾名思义,同步生产者每条消息都会实时发送到 Kafka,而异步生产者则为了提升性能,会等待存了一批消息或者到了指定间隔时间才会一次性发送到 Kafka。
2.Producer API
同步生产者的代码如下:
package main import ( "github.com/IBM/sarama" "kafka_project/conf" "log" "strconv" ) // 本实例展示的同步生产者的使用 func main() { //生产10个消息 Producer(10) } func Producer(limit int) { config := sarama.NewConfig() config.Producer.Return.Successes = true config.Producer.Return.Errors = true producer, err := sarama.NewSyncProducer([]string{conf.HOST}, config) if err != nil { log.Fatal("NewSyncProducer err:", err) } defer producer.Close() var success, errors int for i := 0; i < limit; i++ { str := "生产第" + strconv.Itoa(i) + "条消息" msg := &sarama.ProducerMessage{ Topic: conf.TOPIC, Key: nil, Value: sarama.StringEncoder(str)} partition, offset, err := producer.SendMessage(msg) if err != nil { log.Printf("SendMessage:%d err:%v\n ", i, err) errors++ continue } success++ log.Printf("[Producer] partitionid: %d; offset:%d, value: %s\n", partition, offset, str) } log.Printf("发送完毕 总发送条数:%d successes: %d errors: %d\n", limit, success, errors) }
3.ConsumerAPI
package main import ( "github.com/IBM/sarama" "kafka_project/conf" "log" )
func main() { //单分区消费 SinglePartition() }
func SinglePartition() { config := sarama.NewConfig() consumer, err := sarama.NewConsumer([]string{conf.HOST}, config) if err != nil { log.Fatal("NewConsumer err:", err) } defer consumer.Close() // 参数1 指定消费那个topic // 参数2 分区,这里默信0号分区 // 参数3 offset 从哪儿开始消费起走,正常情况下每次消费完都会将这次的offset提交到kafka // 如果改为 sarama.OffsetOldest 则会从最旧的消息开始消费,即每次重启 consumer 都会把该 topic 下的所有消息消费一次 partitionConsumer, err := consumer.ConsumePartition(conf.TOPIC, 0, sarama.OffsetOldest) if err != nil { log.Fatal("ConsumerParition err:", err) } defer partitionConsumer.Close() // 会一直阻塞在这里 for message := range partitionConsumer.Messages() { log.Printf("[Consumer] partitionid: %d; offset:%d, value: %s\n", message.Partition, message.Offset, string(message.Value)) } }
运行测试:
启动生产端生产消息
启动消费端
反得启动消费端的话,发现消息会重复消费。
由于独立消费没有提交offset的功能,所以需要借助OffsetManager来完成
offsetManager的代码如下
package main import ( "fmt" "github.com/IBM/sarama" "kafka_project/conf" "log" "time" ) func main() { OffsetManager() } func OffsetManager() { config := sarama.NewConfig() // 配置开启自动提交 offset,这样 samara 库会定时帮我们把最新的 offset 信息提交给 kafka config.Consumer.Offsets.AutoCommit.Enable = true //开启自动提交 config.Consumer.Offsets.AutoCommit.Interval = 1 * time.Second // 自动 commit时间间隔 client, err := sarama.NewClient([]string{conf.HOST}, config) if err != nil { log.Fatal("NewClient err:", err) } defer client.Close() // offsetManager 用于管理每个 consumerGroup的 offset // 根据 groupID 来区分不同的 consumer,注意: 每次提交的 offset 信息也是和 groupID 关联的 offsetManager, err := sarama.NewOffsetManagerFromClient("singleId", client) if err != nil { log.Println("NewOffsetManagerFromClient err:", err) } defer offsetManager.Close() // 每个分区的 offset 也是分别管理的,目前本机也只有一个分区topic 只有 1 个分区(这里面是指0分区) partitionOffsetManager, err := offsetManager.ManagePartition(conf.TOPIC, conf.TOPICPARTITION) if err != nil { log.Println("ManagePartition err:", err) } partitionOffsetManager.Close() //在程序结束后提交一次,防止消息丢失 defer offsetManager.Commit() consumer, err := sarama.NewConsumerFromClient(client) if err != nil { log.Println("NewConsumerFromClient err:", err) } // 根据 kafka 中记录的上次消费的 offset 开始+1的位置接着消费 nextOffset, _ := partitionOffsetManager.NextOffset() // 取得下一消息的偏移量作为本次消费的起点 fmt.Println("nextOffset:", nextOffset) pc, err := consumer.ConsumePartition(conf.TOPIC, conf.TOPICPARTITION, nextOffset) if err != nil { log.Println("ConsumePartition err:", err) } defer pc.Close() for message := range pc.Messages() { value := string(message.Value) log.Printf("[Consumer] partitionid: %d; offset:%d, value: %s\n", message.Partition, message.Offset, value) // 每次消费后都更新一次 offset,这里更新的只是程序内存中的值,需要 commit 之后才能提交到 kafka // MarkOffset 更新最后消费的 offset partitionOffsetManager.MarkOffset(message.Offset+1, "modified metadata") } }
运行offsetManager消费端反复消费,可以看到不会重复消费了