kafka官网:http://kafka.apachecn.org/
消息队列在如今的软件架构中,地位非比寻常。优点如下:
1)、解耦。2)、冗余。3)、扩展性。4)、灵活性and峰值处理能力。5)、可恢复性。6)、顺序保证。(ps:kafka保证一个partition内的数据是有序的)7)、缓冲。8)、异步通信。
Kafka是一个分布式的、可分区的、可复制的消息系统。
其基本术语:
这里使用docker安装,首先安装zookeeper
docker run -itd --name zookeeper -p 2181:2181 -v /etc/localtime:/etc/localtime zookeeper:3.6
docker run -itd --name kafka -p 9092:9092 -e KAFKA_BROKER_ID=0 \
-e KAFKA_ZOOKEEPER_CONNECT=192.168.254.172:2181/kafka \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.254.172:9092 \
-e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 \
-v /etc/localtime:/etc/localtime wurstmeister/kafka
略。。。
文档地址:https://godoc.org/github.com/Shopify/sarama#Broker
go get github.com/Shopify/sarama
//集群也可以用下面这个
go get github.com/bsm/sarama-cluster
func InitKafkaProducer()(sarama.AsyncProducer,error){
config := sarama.NewConfig()
config.Producer.RequiredAcks = sarama.WaitForAll //等待服务器所有副本都保存成功后的响应
config.Producer.Partitioner = sarama.NewRandomPartitioner //随机的分区类型
//是否等待成功和失败后的响应,只有上面的RequireAcks设置不是NoReponse这里才有用
config.Producer.Return.Successes = true
config.Producer.Return.Errors = true
config.Version = sarama.V2_5_0_0 //设置使用的kafka版本,如果低于V0_10_0_0版本,消息中的timestrap没有作用.需要消费和生产同时配置
return sarama.NewAsyncProducer([]string{"192.168.254.172:9092"},config)
}
//ProducerMsg 调用该函数生产消息
func ProducerMsg(){
prod,err := InitKafkaProducer()
if err!=nil{
panic(err)
}
defer prod.Close()
msg := &sarama.ProducerMessage{
Topic: "log",
Key: sarama.StringEncoder("test") ,
Partition :1,
}
msgchan := prod.Input()
for i:= 0;i<100;i++{
msg.Value = sarama.StringEncoder("msg id is :" + strconv.Itoa(rand.Intn(100)))
msgchan <- msg
select {
case suc := <-prod.Successes():
fmt.Println(suc)
case err := <-prod.Errors():
fmt.Println(err)
}
}
}
func InitKafkaConsumer()(sarama.Consumer,error) {
config := sarama.NewConfig()
config.Consumer.Return.Errors = true
config.Version = sarama.V0_11_0_0
return sarama.NewConsumer([]string{"192.168.254.172:9092"}, config)
}
//ConsumerMsg 调用该函数消费消息
func ConsumerMsg(){
cons,err := InitKafkaConsumer()
if err!=nil{
panic(err)
}
defer cons.Close()
//获取分区数
res,_ := cons.Partitions("log")
fmt.Println(res)
pc,err := cons.ConsumePartition("log",0,sarama.OffsetNewest)
if err!=nil{
panic(err)
}
msgchan := pc.Messages()
errchan := pc.Errors()
for {
select {
case msg :=<- msgchan :
fmt.Println("msg:",msg.Partition,string(msg.Key),string(msg.Value),msg.Timestamp.String())
case err =<- errchan :
fmt.Println(err)
}
}
}
需求:为一个主题添加两个分区,并指定副本
更多管理方法参考 https://godoc.org/github.com/Shopify/sarama#Broker
func ClusterMge(){
config := sarama.NewConfig()
config.Version = sarama.V2_5_0_0 //版本要高于2.4
admin,err := sarama.NewClusterAdmin([]string{"192.168.254.172:9092"},config)
if err!=nil{
panic(err)
}
//这里添加两个分区,也可以做其他操作
err = admin.CreatePartitions ("log",3,[][]int32{{0},{0}},false)
if err!=nil{
panic(err)
}
admin.Close()
}
上面的消费者例子中,如何同时启用两个消费者,就会发现,每一条消息会被两个接受者接收到。
如果想让一条消息只被一个消费者接收到,就要设置消费者组,同组消费者对于一条消息,只有一个人能够接收到。例子如下:
//先实现接口
type exampleConsumerGroupHandler struct{}
func (exampleConsumerGroupHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
func (exampleConsumerGroupHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
func (h exampleConsumerGroupHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
for msg := range claim.Messages() {
fmt.Printf("Message topic:%q partition:%d offset:%d value=%s\n", msg.Topic,msg.Partition, msg.Offset,string(msg.Value))
sess.MarkMessage(msg, "")
}
return nil
}
//启用消费者组,将程序打包后,运行同样的两个实例,会发现接收到的消息不相同
func StartConsumeGroup(){
config := sarama.NewConfig()
config.Version = sarama.V2_0_0_0 // specify appropriate version
config.Consumer.Return.Errors = true
group, err := sarama.NewConsumerGroup([]string{"192.168.254.172:9092"}, "my-group", config)
if err != nil {
panic(err)
}
defer func() { _ = group.Close() }()
go func() {
for err := range group.Errors() {
fmt.Println("ERROR", err)
}
}()
ctx := context.Background()
for {
topics := []string{"log"}
handler := exampleConsumerGroupHandler{}
err := group.Consume(ctx, topics, handler)
if err != nil {
panic(err)
}
}
}
未完待续