kafka

安装

在window环境下使用docker安装

1.kfk是基于zk的,因此需要先安装+启动zk

# 安装zk
 docker pull wurstmeister/zookeeper
# 启动zk
docker run -d --name zookeeper -p 2181:2181 -e TZ="Asia/Shanghai" --restart always wurstmeister/zookeeper 

参数介绍:
--name:容器名字
-p:端口号(映射到宿主机的端口:zookeeper端口号)
# 安装kfk
docker pull wurstmeister/kafka 
# 启动kfk
docker run -d --name kafka -p 9092:9092 -e KAFKA_BROKER_ID=0 -e KAFKA_ZOOKEEPER_CONNECT=192.168.1.3:2181 -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.1.3:9092 -e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 -e TZ="Asia/Shanghai" wurstmeister/kafka 
# 安装kfkmap
docker run -d \
    -p 8080:8080 \
    -v ~/.Desktop/kfkmap\
    -e DEFAULT_USERNAME=admin \
    -e DEFAULT_PASSWORD=admin \
    --name kafka-map \
    --restart always dushixiang/kafka-map:latest
    
 -v是挂载地址,linux环境下修改下挂载地址  
 通过localhost:端口查看kfk内部情况 

kfkmap

kfk常见知识

broker
代表机器
topic
消费主体,消费者根据topic进行消费
partion
分区,一个主题下有不同的分布,一个消费者组里的消费者只能消费一个分区里的内容,一个消费者组里的不同消费者可以消费同一个主题下不同分区的内容,同一个分区里的的内容是有序的,而不同分区的消息是无序的
生产者通过hash将消息推送到不同的分区上,但是如果hash取的不好可能导致消息倾斜
leader和follower
一个主题的同一个分区里的leader和follower在不同的broker上,宕机了可以通过选举机制恢复,follower可以理解为一种特殊的消费者来消费leader的内容
消息积压
如果消费者和生产者的速度不匹配,如消费者的消费速度远远低于生产者的速度,可能导致消息积压甚至硬盘爆了
在考虑消息积压之前,先看KFK是如何运行的
因为硬盘的内容需要读取到内存里才能计算,如果KFK的写入QPS非常大,而KFK的读非常慢,导致内存一直无法获取读内存页,整个KFK响应非常慢,因此整个服务不可用
主要的问题还是出在读太慢了,因此需要从读的角度进行解决
首先引入本地缓存,支持并发,比如点赞,先用本地缓存进行累加(这也DB操作一次性可以+100等,不会一次+1),然后再引入内存队列,内存队列是list,4个线程从左边取,然后和本地缓存比较是不是最新值,如果是最新值则进行DB操作,反之则抛弃;4个线程快速从右边取出抛掉
取左边的线程因为操作比较慢,因此它的值取了并且相等更接近真实值

KFK的经典问题
1.消费顺序
首先保证同一个topic下的同一个分区的写入是顺序的,其余的情况都是无序的;然后再因为消费速度的问题,单个消费者进行消费大概50ms,一秒的吞吐量太低了,因此需要对一个消费者开多线程进行消费,搞多个阻塞队列用多个线程进行消费,根据ID再做一次hash,将相同id订单hash到同一个阻塞队列进行消费。
弹性扩容的话,可以让线程数通过读取配置文件去读,然后修改配置文件,然后stop消息队列(stop前把积压的数据全部commit),然后通过配置文件重新设定线程数量
2.确认机制
一般来说确认机制都是用ALLACK,即所有的follower都落盘后返回ACK这样就可以保证message不会少
3.幂等问题
KFK的重启以及消费挂掉,会涉及到幂等问题;要么你这个事务支持覆盖写,要么是类似于redis 的string之类,要么就是再搞一个redis支持幂等
消费顺序,确认机制和幂等问题三者都是相互关联的,比如某个消息很重要,并要保证它一定被消费到,那么我们对确认机制选择的是waitforall,即全部follower都落盘后才能发ack,这是针对生产者的;针对消费者的时候,你要保证这个一定被消费到,那么我们offset的提交,一定是在操作完之后才能offset,但是可能会出现重复消费的问题(比如插入数据库之后挂掉,然后再从mq里拉消息进行消费),并且可能某些业务的消费顺序一定是固定的(比如生成订单后才能创建支付订单),出现消费幂等和消费顺序的相关问题
kfk常见问题

KFK实战

kfkdemo

基于go和sarama包

生产者初始化

func InitKafkaProducer(c config.Config) (err error) {
	kfkConfig := c.Kafka
	sacrmaConfig := sarama.NewConfig()
	sacrmaConfig.Version = sarama.V3_0_1_0
	sacrmaConfig.Producer.MaxMessageBytes = 1000000 //一条消息的最大长度
	sacrmaConfig.Producer.Retry.Max = 1             //最大重试次数
	sacrmaConfig.Producer.Idempotent = true
	sacrmaConfig.Producer.Return.Errors = true
	sacrmaConfig.Producer.Return.Successes = true
	sacrmaConfig.Producer.RequiredAcks = sarama.WaitForAll //确认机制,所有的follower收到后才能返回ack
	sacrmaConfig.Producer.Timeout = time.Millisecond * 10  // 等待 WaitForAck的时间
	sacrmaConfig.Producer.Partitioner = sarama.NewHashPartitioner
	sacrmaConfig.Net.MaxOpenRequests = 1 //
	createDbItemProducer, err = sarama.NewSyncProducer([]string{kfkConfig.Address}, sacrmaConfig)
	if err != nil {
		return err
	}
	return nil
}

消费者初始化,需要实现Setup,Cleanup,ConsumeClaim三个接口,consumeClaim是消费内容;

func InitKafkaConsume(c config.Config) (err error) {
	kfkConfig := c.Kafka
	sacrmaConfig := sarama.NewConfig()
	sacrmaConfig.Version = sarama.V3_0_1_0
	sacrmaConfig.Consumer.Offsets.Retry.Max = 1            //最大重试次数
	sacrmaConfig.Consumer.Offsets.AutoCommit.Enable = true // 自动提交
	createDbItemClient, err = sarama.NewConsumerGroup([]string{kfkConfig.Address}, CreateDbItem, sacrmaConfig)
	if err != nil {
		return err
	}
	go func() {
		for {
			createDbItemClient.Consume(context.Background(), []string{CreateUser}, &handlerUser{})
		}
	}()
	return nil
}

type handlerUser struct {
}

func (h *handlerUser) Setup(session sarama.ConsumerGroupSession) error {
	log.Println("StartKafka")
	return nil
}

func (h *handlerUser) Cleanup(session sarama.ConsumerGroupSession) error {
	fmt.Println("Cleanup")
	return nil
}

func (h *handlerUser) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) (err error) {
	for msg := range claim.Messages() {
		user := model.User{Id: 0}
		json.Unmarshal(msg.Value, &user)
		err = dao.CreateUser(DB(), &user)
		if err != nil {
			fmt.Println(err)
		}
		session.MarkMessage(msg, "")//保证一定被消费到,需要msg需要在操作结束后才能提交
	}
	return nil
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值