一、kafka概述
1、是什么?
kafka是分布式的基于发布/订阅的消息队列
2、场景
1、一般用于实时场景
2、特殊的离线场景会用到kafka,flume会和kafka整合
3、基础架构
1、producer:生产者,向kafka中写入消息
2、consumer:消费者,向kafka中拉取消息进行消费
3、topic:主题。工作中一般是一个业务一个主题,为了数据便于管理,便于获取需要的业务数据
4、partition: 分区。topic的一个分段。
partition能够实现数据的分布式存储
partition能够提高吞吐量,提高并发度
partition能够实现kafka的可扩展
5、副本: 提高partition数据的安全性
6、broker: kafka的节点
7、consumer group: 消费者组。组中有多个消费者。消费者的个数 = 分区数
一个partition只能被consumer group中的一个消费者消费
8、leader、follower
leader: partition的一个角色。生产者发送消息找leader,消费者消费消息找leader
follower: partition的一个角色。同步leader数据
9、offset: 偏移量。标识消费者上次消费到了partition的哪个位置。
二、安装以及常用指令
1、topic相关
1、topic创建: kafka-topics.sh --create --topic topic名称 --partitions 分区数 --replication-factor 副本数 --bootstrap-server kafka节点:端口<9092>
--create 代表创建
--bootstrap-server 通过传入kafka节点发现节点所在的集群
2、查看集群所有topic: kafka-topics.sh --list --bootstrap-server kafka节点:端口<9092>
3、查看topic详细信息: kafka-topics.sh --describe --topic topic名称 --bootstrap-server kafka节点:端口<9092>
4、修改topic<只能够修改分区数,而且是只能增加,不能减少>:
kafka-topics.sh --alter --topic topic名称 --partitions 分区数 --bootstrap-server kafka节点:端口<9092>
5、删除topic: kafka-topics.sh --delete --topic topic名称 --bootstrap-server kafka节点:端口<9092>
2、生产者: kafka-console-producer.sh --topic topic名称 --broker-list kafka节点:端口<9092>
3、消费者: kafka-console-consumer.sh --topic topic名称 --bootstrap-server kafka节点:端口<9092>
4、查看kafka log、index数据: kafka-dump-log.sh --files ... --print-data-log
5、查看消费者消费到哪个offset: ./kafka-consumer-groups.sh --group 消费者组名 --all-topics --describe --bootstrap-server hadoop102:9092
三、kafka工作流程以及数据存储
1、工作流程: 看上面的基础架构
2、数据存储结构:
topic:
partition:
segement:
index: log中数据的索引
log: 数据存储文件
timestampindex: log中数据的时间索引
segement命名规则:
partition中第一个segement的文件名肯定是00000000000000000000
后续的segement的文件名 = 上一个segement的最后一个offset +1
3、如何根据offset找到数据?
1、根据offset找到属于哪个segement.
2、根据offset从index文件中找到数据在log中的区间
index中索引是稀疏存储的。每隔4K左右为log中数据建立索引
3、扫描log文件的区间获取数据
4、生产者:
1、分区策略<数据究竟发到哪个分区>:
1、直接指定分区,直接将数据发到对应的分区
2、不指定分区,但是指定key: 将数据发到 key.hashCode % 分区数
3、不指定分区,也不指定key: 随机发送分区,每次发送的时候与上一次不一样
2、数据可靠性<producer发送数据是否到kafka>
如何保证数据可靠性?
通过ack机制保证。
ack=0: leader接收到消息马上返回消息。此种情况可能出现数据丢失
ack=1: leader接收到消息并且落盘才返回消息。此种情况可能出现数据丢失
ack=-1: leader落盘消息并且follower同步完成才返回消息。此种情况可能导致数据重复
如果follower因为网络导致同步很慢,到一定的时间还没有同步完成,会踢出ISR列表。
ISR:与leader同步到一定程度[LEO>=HW]的follower
LEO: 每个副本的最后一个offset
HW: 所有副本最小的LEO
follower故障: 故障解决之后,先清除故障之前的HW之后的数据,重新从leader同步
leader故障: 选举出leader,其余的follower会清除掉HW之后的数据,重新从leader同步
3、exactly-once
三种容错语义:
at-most-once: 最多一次<数据可能丢失>
at-lest-once: 最少一次<数据可能重复>
exactly-once: 有且仅有一次
kafka中通过开启幂等性保证exactly-once<producer不挂>。借鉴mysql的主键思想。
主键: producerid + partition + sequenceNumber
producerid: 在producer端口生成
partition: 分区号
sequenceNumber : 序列号。写到分区的第几条数据
该主键会缓存到partition所在的broker上。后续在发送数据的时候,会对比数据的主键与缓存的主键。
4、消费者
1、消费方式: 通过主动拉取数据进行消费
2、分区分配策略:
1、range:
topic[partition0,parition1,partition2,partition3,partition4] consumer group[consumer1,consumer2]
分配分区分为两步:
1、大致估算每个消费者分配多少分区: 分区数 / 消费者个数 = 5/2 =2
2、计算前面几个消费者多消费一个分区: 分区数 % 消费者个数= 5%2=1
consumer1 [partition0,parition1,partition2]
consumer2 [partition3,parition4]
2、轮询:
topic[partition0,parition1,partition2,partition3,partition4] consumer group[consumer1,consumer2]
consumer1 [partition0,parition2,partition4]
consumer2 [partition1,parition3]
3、offset维护:
0.9版本之前offset维护在zookeeper
0.9版本之后offset维护在kafka内部topic中: __consumer_offsets
5、高效读写
1、分区:
2、顺序写磁盘: 减少磁盘寻址时间
3、pagecache:
1、producer写入的时候先写入pagecache,批量写入磁盘效率更高
2、数据在pagecache中会按照地址进行排序。减少磁盘寻址
3、如果网络够好[生产者与消费者速率相等],数据写入pagecache的时候,消费者直接消费了,不用经过磁盘
4、pagecache不在JVM中,不会增加GC的压力
4、零拷贝
比如将数据从磁盘读取之后写入网络
正常情况:
1、将数据读取到内核的pagecache中
2、将数据从pagecache拷贝到用户内存中进行操作
3、将数据写入内核的socket缓冲区
4、将数据从socket缓冲区写入网卡
零拷贝:
1、将数据读取到内核的pagecache中
4、将数据从pagecache写入网卡
6、zookeeper作用:
选举leader、监听broker都是通过controller来做,依赖zookeeper
流程:
1、kafka集群在启动的时候,会从broker中选一个作为controller.
2、broker会到zookeeper中进行注册</brokers/ids/.>
3、controller监听</brokers/ids/.>,后续broker宕机,就能够马上知道
4、broker上保存的partitition也会在zookeeper上进行注册</brokers/topics/topic名称/partitions/分区号>
5、如果broker宕机,如果该broker上有leader,则选举出新leader。
6、获取/brokers/topics/topic名称/partitions/分区号/state数据,更新ISR信息,leader信息
7、事务
1、producer事务:
在代码中写上transactionid,将transactionid与producerid绑定,保存在kafka内部topic中: __transaction_state
后续就是producer挂了,根据transactionid从内部topic中取得producerid
能够保证主键不变
2、consumer事务: kafka不能保证kafka仅且消费一次。