我以前从未使用过kafka。我有两个访问本地kafka实例的测试Go程序:一个读者和一个作家。我正在尝试调整生产者,使用者和kafka服务器的设置,以获取特定的行为。
我的作家:
package main
import (
"fmt"
"math/rand"
"strconv"
"time"
"github.com/confluentinc/confluent-kafka-go/kafka"
)
func main() {
rand.Seed(time.Now().UnixNano())
topics := []string{
"policymanager-100",
"policymanager-200",
"policymanager-300",
}
progress := make(map[string]int)
for _, t := range topics {
progress[t] = 0
}
producer, err := kafka.NewProducer(&kafka.ConfigMap{
"bootstrap.servers": "localhost",
"group.id": "0",
})
if err != nil {
panic(err)
}
defer producer.Close()
fmt.Println("producing messages...")
for i := 0; i < 30; i++ {
index := rand.Intn(len(topics))
topic := topics[index]
num := progress[topic]
num++
fmt.Printf("%s => %d\n", topic, num)
msg := &kafka.Message{
Value: []byte(strconv.Itoa(num)),
TopicPartition: kafka.TopicPartition{
Topic: &topic,
},
}
err = producer.Produce(msg, nil)
if err != nil {
panic(err)
}
progress[topic] = num
time.Sleep(time.Millisecond * 100)
}
fmt.Println("DONE")
}
我的本地卡夫卡上存在三个主题:policymanager-100,policymanager-200,policymanager-300。它们每个只有1个分区,以确保所有消息在kafka收到它们之前都已排序。我的作者将随机选择一个主题,并发出一条消息,其中包含仅针对该主题递增的数字。完成运行后,我希望队列看起来像这样(主题名称为便于阅读而缩短):
100: 1 2 3 4 5 6 7 8 9 10 11
200: 1 2 3 4 5 6 7
300: 1 2 3 4 5 6 7 8 9 10 11 12
到目前为止,一切都很好。我正在尝试配置事物,以便可以使任意数量的使用者旋转并按顺序使用这些消息。 “按顺序”是指在消息1完成(不只是开始)之前,没有任何用户应该获得主题100的消息2。如果正在处理主题100的消息1,则消费者可以从当前没有正在处理的消息的其他主题中自由消费。如果某个主题的消息已发送给使用者,则整个主题应变为“锁定”状态,直到超时假定该使用者失败或该消费者提交了该消息为止,则该主题将被“解锁”以使其成为下一条消息可供消费。
我的读者:
package main
import (
"fmt"
"time"
"github.com/confluentinc/confluent-kafka-go/kafka"
)
func main() {
count := 2
for i := 0; i < count; i++ {
go consumer(i + 1)
}
fmt.Println("cosuming...")
// hold this thread open indefinitely
select {}
}
func consumer(id int) {
c, err := kafka.NewConsumer(&kafka.ConfigMap{
"bootstrap.servers": "localhost",
"group.id": "0", // strconv.Itoa(id),
"enable.auto.commit": "false",
})
if err != nil {
panic(err)
}
c.SubscribeTopics([]string{`^policymanager-.+$`}, nil)
for {
msg, err := c.ReadMessage(-1)
if err != nil {
panic(err)
}
fmt.Printf("%d) Message on %s: %s\n", id, msg.TopicPartition, string(msg.Value))
time.Sleep(time.Second)
_, err = c.CommitMessage(msg)
if err != nil {
fmt.Printf("ERROR commiting: %+v\n", err)
}
}
}
根据我目前的理解,我可能会做到这一点的方法是正确设置消费者。我已经尝试了该程序的许多不同变体。我试过让所有goroutine共享同一个消费者。我尝试为每个goroutine使用不同的group.id
。这些都不是正确的配置,可以实现我想要的行为。
发布的代码所做的是一次清空一个主题。尽管有多个goroutine,该进程将读取全部100个goroutine,然后移至200个,再移至300个,实际上只有一个goroutine将进行所有读取。当我让每个goroutine具有不同的group.id
时,消息就会被多个goroutine读取,这是我想避免的。
我的示例消费者只是在使用goroutines进行分解,但是当我开始在工作中将这个项目应用到我的用例中时,我将需要它跨多个kubernetes实例工作,而这些实例不会互相交谈,因此使用一旦在2个Kubes上有2个实例,goroutine之间的交互就无法正常工作。这就是为什么我希望让卡夫卡做我想要的看门人。
答案 0 :(得分:0)
通常来说,您不能。即使您有一个使用方消耗了该主题的所有分区的单个使用者,也将以不确定的顺序使用分区,并且不能保证您在所有分区上的总排序。
尝试“键入消息”,认为您可能会发现此案例非常有用。