前言
时隔小一个月,博主又更新啦。本文以chatGPT总结为基础,各种博客或者大佬个人网站的分享为补充,最后推出自己的口语化总结,力求打造一本完善的,体系化的Kafka理论知识宝典。本文分为五个模块,基础名词、进阶名词解析,Kafka的机制以及场景分析,最后还有Kafka源码包的简析,做到有深度还有广度。
正文
Kafka基础名词解析
什么是Kafka,Broker、Topic、 Partition、Producer、Consumer又是什么?
Kafka 是一种高性能、可扩展的分布式消息队列系统,常用于处理海量数据和实时数据流。
- Broker(代理) :Kafka 集群由一个或多个独立的服务器节点组成,每个节点被称为 Broker。每个 Broker 负责消息的存储、接收和转发(接化发) 。它们一起形成了一个分布式消息传递系统。
- Topic(主题) :主题是 Kafka 中消息的类别或数据流的名称。消息通过主题进行分类和发布。你可以将主题视为具有唯一标识符的消息队列。例如,你可以有一个主题用于接收日志消息,另一个主题用于接收用户活动消息。
- Partition(分区) :每个主题可以分成一个或多个分区。分区是主题的物理划分,用于实现数据的并行处理和分布式存储。每个分区都是有序的、不可变的消息序列。分区在磁盘上以文件形式存储,并由 Broker 负责管理。
- Producer(生产者) :生产者是生成消息并将其发布到 Kafka 主题的应用程序或系统。生产者负责将消息发送到特定的主题,并且可以选择将消息发送到特定的分区。生产者将消息发送到 Broker,然后由 Broker 将消息持久化并根据配置的复制策略复制到其他 Broker。
- Consumer(消费者) :消费者是从 Kafka 主题中读取消息的应用程序或系统。消费者订阅一个或多个主题,并从主题的一个或多个分区中读取消息。每个消费者都有一个独立的消费者组标识符,以便多个消费者可以以并行的方式消费主题中的消息。
简单总结一下就是,Kafka是一个常用的高性能分布式消息队列,主要有代理、主题、分区、生产者和消费者这些概念。代理也就是Kafka集群中的每个服务器节点的代称,负责消息的存储、接收和转发。Kafka是基于发布订阅的消息系统,主题就是生产者和消费者建立联系的桥梁。每个主题可以划分至少一个分区,每个分区都是一组有序的消息,同时分区数也代表最大并行度。生产者是生成消息并将其发布到 Kafka 主题的应用程序或系统,消费者则是将消息从Kafka主题中读取的应用程序或系统。
补充一些小众知识:
Kafka进阶名词解析
控制器(Controller)
Kafka 启动是会往 Zookeeper 中注册当前Broker 信息. 谁先注册谁就是 Controller. 读取注册上来的从节点的数据(通过监听机制), 生成集群的元数据信息, 之后把这些信息都分发给其他的服务器, 让其他服务器能感知到集群中其它成员的存在
协调者(Coordinator)
Kafka的Coordinator是负责协调和管理消费者组和生产者事务的关键组件。它确保消费者组能够正确分配和消费分区,并管理生产者事务的正确提交和恢复。
- 注册新的消费者:当一个消费者加入消费者组时,它会将自己注册到消费者组协调器,并接收该消费者组的分配策略。
- 分配分区:当消费者组中的消费者数量发生变化(如有新的消费者加入或旧的消费者离开),消费者组协调器负责重新分配分区给消费者。它使用分区分配策略(Partition Assignment Strategy)来决定如何将分区分配给消费者,以实现负载均衡和容错性。
- 管理消费者组的偏移量(offset) :消费者组协调器还负责跟踪和管理消费者组在每个分区上的消费偏移量。它会将消费者组的偏移量提交到Kafka中,以确保消费者组可以在断开连接或重新平衡后正确地从上次的偏移量继续消费。
- 初始化事务:当生产者开始一个事务时,它会与生产者事务协调器进行交互,以获取一个事务ID和对应的Leader副本。
- 提交事务:生产者在事务完成时将事务的提交请求发送给生产者事务协调器。协调器确保事务中的所有消息都成功写入,并将事务的提交状态写入Kafka日志。
- 事务恢复:如果生产者在事务期间失败或崩溃,生产者事务协调器负责协调恢复过程,以确保事务的完整性。
消费者组(Consumer Group)
消费者组(Consumer Group)用于组织和管理消息的消费者。官方的介绍,消费者组是Kafka提供的可扩展且具有容错性的消费者机制。消费者组中包含多个消费者实例,每个实例都可以并行地消费消息。消息队列系统将消息分发给组中的消费者实例,以便实现高吞吐量和负载均衡。需要注意的是超过分区数的同一消费者组的消费者无法消费消息,所以一般设置消费者数等于分区数,当然也可以将多出来的消费者当作故障转移的备用。
值得一提的是,Kafka用消费者组这个设计同时实现了两个消息队列模型。如果所有实例都属于同一个Group,是队列模型。如果所有实例分别属于不同的Group,那么它实现的就是发布/订阅模型(一对多)。
再平衡(Rebalancing)
再平衡(Rebalancing)是指当消费者组中的消费者实例发生变化时,消息队列系统会重新分配分发给各个消费者实例的消息的过程。新消费者实例加入,或者老实例因为故障退出或者心跳超时离开,都会导致再平衡的发生。再平衡过程中需要一定的时间,并可能导致一些消息在重新分配期间无法处理。
Rebalance过程分为两步:Join和Sync
消息(Message)
一个 Kafka 的 Message 由一个固定长度的 header 和一个可变长度的消息体 body组成。
组成header 部分由一个字节的 magic(文件格式)和四个字节的 CRC32(用于判断 body 消息体是否正常)构成。当 magic 的值为 1 的时候,会在 magic 和 crc32 之间多一个字节的数据:attributes(保存一些相关属性,比如是否压缩、压缩格式等等);如果 magic 的值为 0,那么不存在 attributes 属性
body 是由 N 个字节构成的一个消息体,包含了具体的 key/value 消息,比如:
- Key(可选):消息的键,用于分区和消息的顺序性。如果提供了键,Kafka将根据键的哈希值将消息路由到特定的分区,确保具有相同键的消息被写入和读取到同一个分区中。
- Value:实际的消息内容,通常是字节数组或字符串。这是需要传输和处理的数据。
- Offset:消息在分区中的唯一标识符,表示消息在分区中的位置。消费者可以使用偏移量来跟踪其消费进度,确保不会漏掉任何消息。
- Partition(可选):消息所属的分区编号。如果未指定分区,Kafka将根据生产者配置的分区策略自动分配分区。
- Timestamp(可选):消息的时间戳,表示消息被生产的时间。时间戳可以是消息实际被生产的时间,也可以是在生产者发送消息时显式设置的自定义时间。
- Headers(可选):一组键值对,用于存储与消息相关的元数据。头部可以包含各种自定义信息,如消息的来源、类型、版本等。
TOPIC的TOPIC(__consumer_offsets)
在Kafka中,__consumer_offsets是一个内部主题(internal topic),用于存储消费者组的偏移量(offset)信息。它是用来跟踪消费者在特定主题的分区中消费的进度。__consumer_offsets主题中的每条消息都包含以下信息:
- 消费者组ID(consumer group ID)
- 主题名称(topic name)
- 分区ID(partition ID)
- 消费者组内消费者的偏移量(offset)
- 消息提交的时间戳(timestamp)
通过维护这个内部主题,Kafka能跟踪和管理消费者组在分区中消费的进度,确保消费者能够正确地处理消息并实现故障恢复。当消费者启动时,它会从该主题中读取偏移量信息,确定从哪个偏移量开始消费消息。随着消息的处理和消费者提交偏移量,这些信息也会相应地更新。同时,Kafka的消费者协调器(consumer coordinator)负责更新和维护__consumer_offsets主题。它会处理消费者加入和离开消费者组的请求,以及消费者提交和获取偏移量的操作。
数据文件(Data files)和日志文件(Log files)
数据文件(Data Files):
在Kafka中,数据文件通常指的是Kafka服务器上存储实际消息数据的文件。Kafka使用分段存储的方式来管理消息,每个主题被分成多个分区(partitions),而每个分区又被细分为一系列的数据文件,这些数据文件用于存储消息的内容。当消息被生产者发送到Kafka集群后,它们会被追加到适当分区的数据文件中。消费者可以从这些数据文件中读取消息。
日志文件(Log Files):
在Kafka中,日志文件通常指的是Kafka Broker上用于持久化消息的文件。Kafka使用日志来保持消息的持久性,并使用基于追加的日志存储模型。每个主题的每个分区都有一个相应的日志文件,用于按顺序存储消息。日志文件中的消息是以追加的方式进行写入和读取的。一旦日志文件达到一定的大小限制或时间限制,Kafka会将其关闭并创建一个新的日志文件,以便继续持久化消息。
日志索引
Kafka 能支撑 TB 级别数据, 在日志级别有两个原因: 基于顺序追加写日志 + 稀疏哈希索引(和数据文件类似,都用了索引这个概念来优化)。Kafka的稀疏哈希索引是一种以固定间隔记录关键消息偏移量的数据结构,用于加速消息的定位和检索,同时节省内存空间。
当消息被写入Kafka的日志文件时,它们按照顺序追加到分区的日志末尾。每个分区都有一个对应的稀疏哈希索引,其中记录了一些重要的消息偏移量。这些索引并不会记录每条消息的偏移量,而是以固定的间隔(通常是一段连续的消息)记录一些关键消息的偏移量。使用稀疏哈希索引的好处在于,它能够显著减少索引的大小,从而节省了内存空间。
当消费者需要读取特定偏移量的消息时,它可以首先通过稀疏哈希索引找到最接近目标偏移量的记录,然后再线性地扫描日志文件,直到找到目标消息。这种方法虽然相比完整的索引查找要慢一些,但是稀疏哈希索引的存在使得整体的查找速度仍然非常高效。
Kafka机制解析
详细说明一下Kafka的多副本(Replica)机制
Kafka的副本(Replica)提供了数据冗余和故障恢复的机制,确保Kafka节点故障时不会丢失数据,并支持高吞吐量的数据读写操作。
每个分区可以拥有多个副本,多个副本分布在不同的Broker节点上。一个分区的第一个副本称为领导者副本(Leader Replica),其他副本称为追随者副本(Follower Replica)。
领导者副本:每个分区的领导者副本负责处理该分区的所有读写请求。生产者将消息发送到领导者副本,消费者从领导者副本读取消息。领导者副本还负责将数据复制到追随者副本。
追随者副本:追随者副本是领导者副本的复制品。它们会从领导者副本同步数据。追随者副本不处理客户端的读写请求,仅用于提供冗余和故障转移。 (值得一提的是,很多分布式系统在设计类似副本概念的时候,会至少提供读的功能以分摊主节点压力,相比之下Kafka的副本机制更加严格)
数据复制:领导者副本将消息写入其本地日志(Log),然后通过复制机制将消息复制到追随者副本的日志中。复制可以使用两种模式:同步复制和异步复制。
- 同步复制:领导者副本等待所有追随者副本确认已成功复制消息后,才认为消息已提交。这种模式提供最强的数据保证,但会对写入延迟产生一定影响。
- 异步复制:领导者副本在将消息写入本地日志后立即返回成功响应,而不等待追随者副本的确认。这种模式可以提供更低的写入延迟,但在某些情况下可能会出现数据丢失。
副本同步:为了保持追随者副本与领导者副本的一致性,Kafka使用了基于日志的复制机制。追随者副本从领导者副本复制日志条目,并按顺序将它们追加到自己的日志中。复制过程采用高效的增量拉取方式,只拉取尚未复制的日志段。
故障转移:当领导者副本发生故障时,Kafka会自动选举一个追随者副本作为新的领导者。分区中的所有副本统称为 AR(Assigned Replicas),选举过程中,Kafka使用分区的ISR(In-Sync Replica)集合,即与领导者副本保持数据同步的追随者副本集合,延迟的追随者存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。只有在ISR中的追随者副本才有资格成为新的领导者。
- 如果故障的是领导者副本,Kafka会从ISR中选择一个追随者副本成为新的领导者。注意,如果ISR全部宕机,会从AR中选择第一个应答的副本当成leader,因此会造成消息丢失或者重复。
- 如果故障的是追随者副本,Kafka会将其从ISR中移除,并继续与其他追随者副本保持同步。
简单总结一下,Kafka的副本机制提供了数据冗余和故障恢复的功能,每个分区可以有多个副本并且可以分散在不同的节点上。副本的角色划分为领导者和追随者,领导者负责真实的读写以及将数据同步给追随者副本,有同步和异步两种模式。追随者仅负责数据冗余和故障转移恢复。所有副本统称AR,追随者和领导者数据同步一致的集合被称为ISR,其他为OSR,如果领导者出现故障则从ISR随机选择一个副本成为领导者,如果ISR挂了,就选择AR中第一个应答的副本。
副本分片规则
Kafka分配Replica的算法有两种: RangeAssignor 和 RoundRobinAssignor。默认为RangeAssignor,也可以自定义设置分片位置。
- 将所有Broker(假设共n个Broker)和待分配的Partition排序
- 将第i个Partition分配到第(i mod n)个Broker上
- 将第i个Partition的第j个Replica分配到第((i + j) mod n)个Broker上
Kafka 的多分区(Partition)以及多副本(Replica)机制有什么好处呢?
Zookeeper 在 Kafka 中的作用知道吗?
总的来说,Zookeeper在Kafka中承担了协调、配置管理、分区分配、副本管理和客户端会话管理等重要的角色,确保了Kafka集群的稳定运行和数据一致性。
Kafka 为什么那么快
简单总结下,一是多分区的分布式结构使得多线程的利用十分充分。二是利用操作系统的文件映射机制,直接将磁盘数据映射到内存,减少了一次复制开销,提升了数据读写性能。三是支持批量处理,生产者和消费者同时支持,减少了网络开销和请求次数。四是磁盘顺序写,磁盘写的效率比随机写更高。五是热门消息存在内存,提高读写性能。
分享一点冷门小知识
Kafka为什么不自己管理缓存, 而非要用page cache?
Kafka性能篇:为何Kafka这么"快"?,这一篇我看了,感觉写得很不错,特意贴一下。如果想要详细理解,可以跳链接看一下
Kafka场景分析
Kafka如何保证高可用
简单来说,分为四点。一是Kafka的分布式结构,多分区分布在不同节点,部分节点宕机依然能正常提供服务。二是多副本机制通过数据冗余来保障高可用。三是Kafka集群的控制器角色会管理整个集群的状态和元数据,一旦某个节点上线或者下线被监听到,会进行分区和副本领导权的重分配。四是Kafka集群中的协调者,在生产端保障数据的事务操作,并在消费端通过再平衡机制确保数据被消费者组顺利消费。
Kafka如何保证消息不重复消费
简单来说,Kafka主要是通过消费者偏移量来确保消息不被重复消费。Kafka内部通过协调者和内部TOPIC(__consumer_offsets)来确保消费者实例不会重复消费。开发者则可以通过手动提交消费偏移量的方法,来保证消费失败或者出现异常时,主动不提交偏移量,从而避免重复消费。开发者还可以通过在消费端进行幂等处理来规避重复消费。
Kafka如何保证消息的消费顺序?
Kafka通过分区(partitions)和分区内的偏移量(offsets)来保证消息的顺序。主题可以是多个分区,但是每个分区中的数据是顺序的,生产者发送的消息顺序追加至分区末尾,消费者也是顺序消费数据。
也就是说,如果一个主题有多个分区,消息的顺序只在分区内得到保证,而不同分区之间的消息顺序无法被Kafka保证。如果应用程序的逻辑需要严格的全局顺序,那么所有相关的消息应该被发送到同一个分区,或者应用程序在消费者端进行额外的处理来实现全局顺序。
Kafka 如何保证消息不丢失
简单总结下,Kafka多副本冗余数据的机制可以在多个节点拥有多个备份使用。并且ACK机制,确保生产者发送消息后,可以选择部分或者所有副本都收到消息,才确认结束请求,类似于事务机制。消费者通过偏移量的主动提交,同样能避免消息不被重复消费或丢失
消息积压怎么处理
简单总结下,一般情况下都是消费压力大,可以通过临时扩充分区和消费者数量的方式提高消费速度。后续按照我自己的经历消息积压问题难?思路代码优化细节全公开,进行项目难点总结。
Kafka源码
生产端
NIO 网络通信模块:Kafka 是如何基于Java NIO 实现一套工业生产级的网络底层通信模块
内存缓冲池设计:Kafka 客户端是如何设计一套支撑百万并发的高吞吐量的缓冲机制
Sender发送线程:重点的重点,Kafka 客户端是如何通过网络通信把一批批消息发送到 Kafka Broker 端上去的?这里涉及到很多网络通信的细节,一些参数设置,应对网络故障时的处理
集群元数据拉取和更新机制:集群元数据拉取组件以及拉取时机;元数据如何在客户端缓存的;如何对Topic元数据支持细粒度的按需加载和同步等待。
服务端
集群架构:Kafka Broker 的集群架构是如何实现的;各个 Broker启动起来后是如何组成一个集群的;集群的 Controller 是如何被选举出来的;故障恢复高可用架构方式是如何实现的等等。
服务端网络通信模块:Kafka 服务端网络通信模块是如何实现的、了解 Reactor 设计模型以及 Kafka 基于 Reactor 实现的支撑超高并发的网络架构、深度剖析 Acceptor 线程、Processor 线程、RequestChannel、IO线程池等网络底层通信组件实现以及请求处理全流程源码。
分区与副本:多副本冗余以及高可用架构如何实现;Leader 和 Follower 数据如何同步、副本如何传输、之间的HW和LEO如何变更;Leader 所在 Broker 故障宕机后, 如何保证系统高可用;副本管理器如何管理副本;Broker是如何异步更新元数据缓存的等等。
负载均衡与伸缩架构:如何保证数据均衡的分布在集群的 Broker 机器上的;如何进行 Topic 的 Partition 的扩缩容以及 Broker 的扩缩容。
日志存储架构:Kafka 是如何高效存储的;磁盘读写是如何实现的;日志的存储结构是怎样的;如何利用 OS Cache、零拷贝、稀疏索引、顺序写等优秀设计来支撑超高吞吐量的存储架构的。
消费端
消费流程:消费端是如何初始化的;如何与服务端进行通信的;Consumer 的 poll() 方法是如何进行数据消费的。
消费者组管理:Consumer Group 概念、状态机流转;Consumer启动后是如何加入到 Group 的;Consumer Group 管理全流程;Consumer Group 元数据管理设计原理等等。
Coordinator 机制:Consumer Coordinator 工作原理;Consumer Coordinator 是如何选举出来 Consumer Leader 的;Consumer Leader 如何制定分区分配方案;Consumer Coordinator 又是如何下发分区分配方案的; 以及 Consumer 是如何定时发送心跳给 Consumer Coordinator 的等等。
消息重平衡机制:几种重平衡场景;以及重平衡源码流程分析。
__consumer_offsets:topic中的topic。
订阅状态与Offset操作:消费端订阅状态是如何保存和追踪Topic Partition 和 Offset 的对应关系;以及了解 Offset 如何获取以及提交方式等等。
如何设计一个消息队列?
设计一个消息队列时,您可以考虑以下关键方面:
设计一个消息队列需要综合考虑多个因素,并根据实际需求和应用场景做出决策。这些指导原则可帮助您开始设计一个可靠、高性能和可扩展的消息队列系统。
写在最后
刚刚看了下,上一篇文章是6月27日发布的,这一篇是7月23日写完了,大体是21日写完的,修修补补了小半天,周一发,也就是时隔一月。这个更新频率确实有点慢了,后续会继续投入时间进来。博主这接近一个月的时间呢,哈哈,发生了很多很多事情,除了上一周平平淡淡,其他两周对我来说都充满了刺激和惊喜。我呢,被激活了新的目标和动力,以后会好好平衡工作、生活和学习,这个世界很有意思,我说真的。
我有懒惰,说实话,这几周的时间,学习的时间不太够吧,更多放在工作和生活上了,包括上周也是加班来着,很烦。生活上呢,可能是心态再次改变吧,最近不知道为啥,看过很多励志的话,并且做出了我之前从来不敢做的事情,很大胆,我说真的。结果挺好的,反正我很高兴,哈哈,很奇妙的感觉,我觉得我心态更加积极了。无论结果如何,我将不违心地做到更好,加油加油!重申一遍我的人生信条,我要让这痛苦压抑的世界绽放幸福快乐之花,向美好的世界献上祝福!!