Kafka工作流程概述

image.png

Kafka中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。

topic是逻辑上的概念,一般一个业务就是一个topic,而partition是物理上的概念,每个partition对应于一个log文件,该log文件中存储的就是producer生产的数据。Producer生产的数据会被不断追加到该log文件末端,且每条数据都有自己的offset。消费者组中的每个消费者,都会实时记录自己消费到了哪个offset,以便出错恢复时,从上次的位置继续消费。
每个partition都有自己的Leader和Follower.

工作流程详细分析

发送数据

follower的同步流程:


image.png

  1. Producer在写入数据的时候永远的找leader,不会直接将数据写入follower
    2. 消息写入leader后,follower是主动的去leader进行同步的!
    3. producer采用push模式将数据发布到broker,每条消息追加到分区中,顺序写入磁盘,所以保证同一分区内的数据是有序的
    不存在的topic写数据,kafka会自动创建topic,分区和副本的数量根据默认配置都是1。

分区:

生产者是怎么把数据给我们分区的?

ACK应答机制:

数据可靠性保证ACK机制

保存数据

Kafka初始会单独开辟一块磁盘空间,顺序写入数据(效率比随机写入高),将数据保存在磁盘。

任何发布到 Partition 的消息都会被追加到 Partition 数据文件的尾部,且消息消费后不会删除(删除策略是针对过期的 Segment 文件),这样的顺序写磁盘操作让 Kafka 的效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是 Kafka 高吞吐率的一个很重要的保证)。

Partition 结构:


 Partition在服务器上的表现形式就是一个一个的文件夹,每个partition的文件夹下面会有多组segment文件,每组segment文件又包含.index文件、.log文件、.timeindex文件(早期版本中没有)三个文件, log文件就实际是存储message的地方,而index和timeindex文件为索引文件,用于检索消息。
image.png

如上图,这个partition有三组segment文件,每个log文件的大小是一样的,但是存储的message数量是不一定相等的(每条的message大小不一致)。文件的命名是以该segment最小offset来命名的,如000.index存储offset为0~368795的消息,kafka就是利用分段+索引的方式来解决查找效率的问题

Message结构:

上面说到log文件就实际是存储message的地方,我们在producer往kafka写入的也是一条一条的message,message主要包含消息体、消息大小、offset、压缩类型……等等
offset:offset是一个占8byte的有序id号,它可以唯一确定每条消息在parition内的位置!
消息大小:消息大小占用4byte,用于描述消息的大小。
消息体:消息体存放的是实际的消息数据(被压缩过),占用的空间根据具体的消息而不一样。

存储策略:


  无论消息是否被消费,kafka都会保存所有的消息(存在磁盘)。那对于旧数据有什么删除策略呢?
    基于时间,默认配置是168小时(7天)。
    基于大小,默认配置是1073741824。
  需要注意的是,kafka读取特定消息的时间复杂度是O(1),所以这里删除过期的文件并不会提高kafka的性能

消费数据

Kafka采用的是点对点的模式,消费者主动的去kafka集群拉取消息,与producer相同的是,消费者在拉取消息的时候也是找leader去拉取

  1. 多个消费者可以组成一个消费者组(consumer group),每个消费者组都有一个组id。
    2.同一个消费组的消费者可以消费同一topic不同分区的数据,但是不会组内多个消费者消费同一分区的数据!!!
    3. 消费者数少于分区:会出现某个消费者消费多个partition数据的情况(此时消费的速度不及只处理一个partition的消费者的处理速度)
    4.消费者数多于分区:多出来的消费者不消费任何partition的数据。

建议消费者组的consumer的数量与partition的数量一致!

搜索数据

假如现在需要查找一个offset为368801的message是什么样的过程呢?用一个例子来解释一下搜索过程
image.png

  1. 先找到offset的368801message所在的segment文件(利用二分法查找),这里找到的就是在第二个segment文件。
    2.打开找到的segment中的.index文件(也就是368796.index文件,该文件起始偏移量为368796+1,我们要查找的offset为368801的message在该index内的偏移量为368796+5=368801,所以这里要查找的相对offset为5)。利用二分法查找相对offset小于或者等于指定的相对offset的索引条目中最大的那个相对offset,所以找到的是相对offset为4的这个索引。
    3. 根据找到的相对offset为4的索引确定message存储的物理偏移位置为256。打开数据文件,从位置为256的那个地方开始顺序扫描直到找到offset为368801的那条Message。

注意该 index 文件并不是从0开始,也不是每次递增1的,这是因为 Kafka 采取稀疏索引存储的方式,每隔一定字节的数据建立一条索引,它减少了索引文件大小,使得能够把 index 映射到内存,降低了查询时的磁盘 IO 开销,同时也并没有给查询带来太多的时间消耗。
  小结:这套机制是建立在offset为有序的基础上,利用segment+有序offset+稀疏索引+二分查找+顺序查找等多种手段来高效的查找数据!至此,消费者就能拿到需要处理的数据进行处理了。

消费者记录位置的方式

早期的版本:消费者将消费到的offset维护zookeeper中,consumer每间隔一段时间上报一次,这里容易导致重复消费,且高并发时和ZK频繁交互,性能不好!
新的版本:消费者消费到的offset已经直接维护在kafk集群的__consumer_offsets这个topic中!

参考:

尚硅谷Kafka和 https://www.cnblogs.com/riches/p/11426988.html