- Kafka 如何进行复制
- Kafka 如何处理来自生产者和消费者的请求
- Kafka 的存储细节,比如文件格式和索引。
集群节点
节点ID
- broker启动的时候,它通过创建临时节点把自己的ID注册到ZooKeeper中,当有broker加入或退出集群时,这些节点就可以获得通知。
- 关闭broker时,节点也会消息,但是ID还继续存在,若再启用相同ID的全新broker,它会拥有相同的分区和主题。
控制器
控制器是一个broker,在zk中创建临时节点/controller成为控制器,它主要用于分区首领选举。
-
若控制器关闭或者zk断开连接,zk临时节点消失,集群其他broker通过watch对象收到通知后,它们会尝试让自己成为新控制器。每个选举出来的控制器都会在zk中通过条件递增操作获得一个全局最大的controller epoch,其他节点若收到比较旧的epoch会忽略它们
-
若控制器发现一个broker离开集群,此时失去首领的分区需要一个新首领,然后在分区副本中确定谁是新首领,新首领确定后开始处理外部请求,其他分区开始从新首领复制消息。
-
若控制器发现一个broker加入集群,他会使用brokerID 检查是否存在现有分区的副本,若有就会发变更通知
总结:Kafka使用zk的临时节点来选举控制器,并在节点加入集群or退出集群时通知控制器。然后控制器负责在节点加入or离开时进行分区首领选举。控制器使用epoch来避免脑裂(两个节点同时认为自己是当前控制器)
复制
一个分布式,可分区的,可复制的提交日志服务。
复制作用:保证在个别节点宕机的情况下仍然可以保证可用性,持久性
Kafka使用主题来组织数据,每个主题被分为若干分区,每个分区有多个副本,副本被保存在broker
- 首领负责处理请求响应
- 跟随者负责复制数据,保持数据一致性。
- 首选首领是创建主题时指定的,分区的副本清单里可以很容易找到首选首领
处理请求
Kafka提供了一个基于TCP的二进制协议,指定请求的格式
broker按照请求的顺序来处理,保证了kafka保存的消息是有序的
三种请求/消息
a) 生产请求:客户端写入broker的消息
b) 获取请求:消费者,跟随着副本从broker读取消息
c) 元数据请求:包含客户端感兴趣的主题列表,服务端返回主题-分区-副本-首领等元数据信息
- 监听的每个端口都运行一个Acceptor线程,Acceptor线程创建连接交给Processor线程,Processor线程从客户端获取请求消息放入请求队列,从响应队列获取响应消息发送给客户端。
- Kafka客户端要自己负责将生产请求and获取请求发送到正确的broker上,通过元数据请求获取主题的元数据,然后缓存到客户端,可通过metadata.max.age.ms来配置刷新元数据的频率
- 当生产请求到达分区的首领之后,broker检查主题权限和acks配置参数,如果是0,1则直接返回,如果是all则将请求保存到缓冲区中,直到首领发现所有跟随者副本都复制了该消息,再返回客户端响应
- 当**获取请求(请求某主题具有特定偏移量的消息时)到达分区的首领时,然后使用零复制技术(直接把文件发送到网络通道,而无需保存在本地缓存中)**向客户端响应数据。客户端可以设置broker返回数据的上限防止耗尽客户端内存,设置broker返回下限减少CPU和网络开销,当然不会让客户端一直等待下去,设置超时时间解决这个问题。
物理存储
Kafka的基本存储单元是分区,分配到broker的目录上。
分区分配
创建主题时,kafka如何在broker间分配分区。例如6个broker,创建一个包含10个分区的主题,复制系数为3
目标
a) broker间尽可能的均匀的
b) 确保每个分区的副本分布在不同broker上
c) 尽可能将分区的副本分布在不同机架上
按照数字顺序来选择 broker ,按照交替机架的方式来选择 broker
文件管理
kafka为管理员为每个主题都配置了数据保留期限
由于在一个大文件查找和删除消息是很费时的,所以将文件分成若干个片段
文件格式
除了键、值和偏移量外, 1肖息里还包含了消息大小、校验和、消息格式版本号、压缩算能 (Snappy、 GZip 或 LZ4)和时间戳(在 0.10.0 版本里引入的)
索引
消费者可以从任意偏移量位置 开始读取消息。
例如读取从偏移量100开始的1MB数据
索引:将偏移量映射到片段文件和偏移量在文件里的位置
清理日志
每个日志片段={干净的部分,污浊的部分}
如果在 Kafka 启动时启用了清理功能(通过配置 log.cleaner.enabled 参数),每个 broker 会启动一个清理管理器线程和多个清理线
清理线程在创建好偏移量map 后,开始从干净的片段处读取消息,从最旧的消息开始,把 它们的内容与 map 里的内容进行比对。
检查消息的键是否存在于 map 中,如果不存在, 那么说明消息的值是最新的,就把消息复制到替换片段上。 如果键已存在,消息会被忽略, 因为在分区的后部已经有一个具有相同键的消息存在。在复制完所有的消息之后,我们就将 替换片段与原始片段进行交换,然后开始清理下一个片段。完成整个清理过程之后,每个键 对应一个不同的消息这些消息的值都是最新
删除消息
为了彻底把一个键从系统中删除,应用程序必须发送一个值为null的消息,称为墓碑消息
清理线程首先进行常规清理,消费者看到这个墓碑消息,就知道已经被删除
只有消费者见到墓碑消息之后,清理线程才会将这个键从kafka中移除。
前提是留给消费者足够时间,让消费者见到墓碑消息