深入理解Kafka系列(四)--Kafka的复制存储以及请求处理过程

系列文章目录

Kakfa权威指南系列文章

前言

本系列是我通读《Kafka权威指南》这本书做的笔录和思考。

正文

集群成员关系

Kafka使用zookeeper来维护集群成员的关系。每个broker都有一个唯一的标识符(也就是clientId,也可以自动生成)。

  1. 当启动一个broker的时候,broker通过在zookeeper上创建一个临时节点,把自己的ID注册上去。路径为:/brokers/ids。因此当broker加入or退出集群的时候,这些成员就会收到相关通知。
  2. 如果已经启动了一个ID为A的broker,还要启动一个ID为A的broker。那么这个broker会得到一个错误。(不可同时存在两个ID相同的broker
  3. 如果上述ID为A的broker完全关闭后,再启动一个新的broker(ID也为A),那么新的broker会立刻加入到集群当中,并且拥有与旧broker相同的分区和主题。

控制器

控制器本质上就是一个broker,只不过他跟其他普通broker相比而言,还具有分区首领选举的功能。

控制器产生的标准是什么?

第一个启动成功的broker会成为kafka的控制器,它会在zookeeper上创建一个临时节点/controller。

而后期的broker在启动的时候,也会尝试去zookeeper上创建临时节点/controller,但是他们会收到一个“节点已存在”的异常,然后意识到这个集群中已经有个控制器了,然后他们会在控制器节点上创建Zookeeper Watch对象,以便他们可以收到这个节点变更的通知。

如果控制器和zookeeper断开连接,后续会发生什么?

1.那么zookeeper上的临时节点就会消失。
2.然后集群中其他的broker会通过watch对象得到控制器节点消失的通知。他们会尝试让自己成为新的控制器。
3.然后遵循上述标准,第一个在zookeeper上成功创建临时节点的broker会成为新的控制器,一旦临时节点创建成功,其他正在尝试成为新控制器的broker会收到相应的异常。

扫描二维码关注公众号,回复: 12238535 查看本文章

那会不会有两个控制器的情况?如果有,怎么办?

首先是有的。
例:假设原本的控制器因网络堵塞这一类的原因,导致它被认定为挂掉了,那么临时节点一旦消失,其他的broker不就开始竞争成为控制器了。当新的控制器产生了,原本老的控制器网络又恢复正常了。那就出现了典型的问题:
脑裂问题

kafka是如何解决脑裂问题的呢?

1.回到之前的步骤,如果一个控制器挂了,其他的broker选举出新的控制器后,肯定需要在新的控制器上创建新的watch对象。

2.然后新的控制器会通过zookeeper的条件递增操作获得一个全新的、数值更大的controller epoch,可以理解为数据库的主键。

3.那如果其他的broker在知道当前最新的controller epoch后,后续收到控制器发出的包含旧的epoch的消息,那么就会忽略他们。


Kafka复制

Kafka的复制功能可以算是Kafka架构的一个核心了。
这里简单概括下Kafka的组织架构。

  1. 用主题(topic)来组织数据。
  2. 每个主题被分为若干个分区。(分区可以理解为平摊数据量,分开存储)
  3. 每个分区有多个副本。(副本可以理解为复制数据,用于高可靠)
  4. 而副本就是保存在broker上的。

副本的类型:

  1. 首领副本

1.上文说过,每个分区可能有多个副本。
2.但是只有一个副本是首领副本。
3.所有生产者和消费者请求都经过首领副本。

  1. 跟随者副本

1.首领副本以外的副本全部都是跟随者副本。
2.跟随者副本不处理任何请求。
3.他们唯一的作用就是从首领副本那里复制消息,保持一致性。 并且在首领副本崩溃的时候,选举一个新的首领副本。


这里解释下为什么跟随者副本不处理任何请求。

1.假设某用户先后进行两次同样的消费请求。有两个跟随者副本,一个A一个B,并且A已经和主副本同步,B没有。
2.第一次从跟随者副本A当中读取,读取到的消息为hello。
3.第二次从跟随者副本B当中读取,读取到的消息为hello world。
4.那么问题来了,第二次读到的数据是脏数据,实际数据是A,这样造成了数据的不一致性。因此Kafka直接干脆的让所有的消费请求都经过主副本中。


那么问题又来了,如果一个主副本挂掉了,其他的从副本进行选举,那怎么保证选举出来的副本,保存的数据一定是完整的?

在回答这个问题之前,先讲下两个概念,同步or不同步的副本。

  1. 不同步副本

1.首先说下前提,为了保持和首领副本同步,跟随者需要向首领副本发送获取数据的请求。(可以看做消费者消费请求)。
2.首领会把响应消息发送给跟随者,而这些请求消息里面,包含了跟随者想要获取消息的偏移量,并且偏移量有序。
3.如果说跟随者在10s内没有请求任何消息or10s内没有请求最新的数据,那么,这个副本被认定为不同步的。
备注:这个10s可以通过配置replica.lag.time.max.ms参数来指定

  1. 同步副本

持续请求得到的消息总是最新的,这样的副本叫做同步的副本。

那回到之前的问题:如何保证数据是完整的?

Kafka中,当首领发生失效的时候,只有同步副本才有可能被选为新的首领。


处理请求

  1. 每一个broker会在它所监听的每一个端口上运行一个Acceptor线程,这个线程会创建一个连接,并把这个连接交给Processor线程去处理。
  2. Processor线程负责从客户端获取请求消息,把它们放进请求队列,然后从响应队列中获取响应消息,把他们发送给客户端。

大致流程如下图:
在这里插入图片描述
图中的IO线程有两种常见的类型:(IO线程负责处理队列的消息)

  1. 生产请求:生产者端发送的请求,包含客户端要写入broker的消息。
  2. 获取请求:消费者和跟随者副本需要从broker中读取消息时发送的请求。

这里我们应该注意到,上面两种请求都必须发送给分区的首领副本。

问题1:如果说某一个broker收到了一个针对特定分区的请求,但是该分区的首领在别的broker上,那怎么办?

答:那么发送请求的客户端会收到一个”非分区首领“的错误响应。

因此,Kafka客户端需要自己负责把请求发送到正确的broker上。

问题2:客户端怎么知道该往哪里发送请求呢?

答:客户端使用了元数据请求的请求类型
该请求包含的内容包含客户端感兴趣的主题列表,而一般上,服务器端响应的消息中会指明主题所包含的分区、分区的副本分布情况、哪个副本是首领等等。
并且,元数据请求可以发送给任意一个broker,因为所有broker都缓存了这些信息。


生产请求

回顾之前讲的内容,生产者有个参数配置叫acks

  1. acks=1:只要首领收到消息就认为消息写入成功。
  2. acks=all:需要所有的同步副本收到消息才算写入成功。
  3. acks=0:生产者发出消息后,不需要等待broker回应。

那么包含首领副本的broker在收到生产请求时,会对请求做出以下步骤:

  1. 权限校验:发送数据的用户是否拥有主题写入的权限?
  2. 参数校验:acks的值是否有效?(只能0,1,all)
  3. 副本校验:如果acks=all,是否有足够多的同步副本保证消息已经被安全写入?(如果数量不足,kafka可以拒绝写入)
  4. 消息写入:写入本地磁盘。
  5. 检查参数:消息写入到分区首领后,检查acks参数为多少。如果acks=0或者1,broker立即返回响应。如果acks=all,将请求保存到一个炼狱的缓冲区,直到首领发现所有跟随者副本都复制了消息,才会把响应返回给客户端。

获取请求

broker中处理获取请求的方式和生产请求的方式很相似。一句话概括:客户端发送请求,向broker请求主题分区里具有特定偏移量的消息。

先放图:
在这里插入图片描述
大概步骤就是:

  1. 请求发送到指定的分区首领上,然后客户端通过查询元数据来确保请求的路由是否正确。
  2. 检查请求是否有效:偏移量在分区上是否存在等问题。
  3. 若请求的偏移量存在,broker按照客户端指定的数量上限从分区里读取消息,再返回给客户端。

这里需要注意这么几个点:
1.Kafka使用零复制技术向客户端发送消息:即直接把消息从文件里发送到网络通道,而不需要经过任何中间的缓冲区。
2.Kafka客户端一般会设置broker返回数据的上限和下限,用于控制broker一次返回的数据量。
3.也就是说如果数据量没有到达下限,broker的请求发送出去后,需要等到数据量足够的时候才会接收到消息。
4.当然客户端不会一直等待broker积累数据,而是在一定时间内。若数据量依旧不达标,也会把数据返回。

  1. 返回的数据为同步数据。

在这里插入图片描述


物理存储

Kafka的基本存储单元是分区,在配置Kafka的时候,一般会指定一个用于存储分区的目录地址,也就是log.dirs这个参数(server.properties,注意不要和log.properties搞混)

接下来我们就看Kafka是如何进行分区分配存储数据的。

分区分配

Kafka在创建主题的时候,就会决定如何在broker之间分配分区。

例:我们有6个broker,打算创建一个包含10个分区的主题,复制系数为3(副本量),则总共会有10*3=30个分区副本,被分配给6个broker。而进行分区分配的时候,Kafka需要要求以下几点:

  1. 分区副本均匀分布在broker上。(相当于平均每个broker有5个分区副本)
  2. 确保每个分区的不同副本分布在不同的broker上。(每个分区有3个副本,其中有主副本和跟随者副本),意思是主和从副本需要分配在不同的broker上,不可以同一个分区的多个副本在同一个broker上。参考下图

在这里插入图片描述

文件管理和索引

Kafka管理文件的方式有两种:存储时间、存储大小。详细可以看生产者篇的参数。

一般Kafka把分区分成若干个片段,**默认情况下,每个片段包含1GB或者一周的数据,并且以较小的那个为准。**在broker往分区写入数据的时候,如果达到片段上限,就关闭当前文件,并打开一个新的文件。

当前正在写入数据的片段叫做活跃片段,并且活跃片段永远不会被删除。

那么,写入的文件格式是咋样的呢?

我们知道,Kafka把消息和偏移量保存到文件里面,那么文件还包含哪些内容?
在这里插入图片描述
除了键值对以及偏移量外,消息里面还包含了消息的大小、校验和、消息格式版本号、压缩算法(Snappy,Gzip,Lz4)以及时间戳。时间戳指的是消息到达broker的时间。但是可以注意到,上图右下角部分是黑色的,这一部分代表生产者发送的是压缩过的消息。,那么同一个批次的消息会被压缩在一起,被当做包装消息进行发送。

使用Kafka自带的一个工具,即可看到片段的内容(消息内容)

./bin/kafka-run-class.sh kafka.tools.DumpLogSegments --deep-iteration 文件

消费者可以从Kafka的任意可用偏移量位置开始读取消息,那么消费者是怎么进行定位呢?

答:利用索引。
Kafka为每个分区维护了一个索引,索引把偏移量映射到片段文件和偏移量在文件里的位置。当然,索引也会被分成片段,所以删除消息的时候,也会删除掉相应的索引。

文件的清理和工作原理

Kafka的每个日志片段可以分为俩部分。

  1. 干净的部分:这些消息之前被清理过,每个键只有一个对应的值,这个值是上次清理时保留下来的。
  2. 污浊的部分:这些消息是在上一次清理之后写入的数据。
    如图:
    在这里插入图片描述

Kafka可以自己配置清理功能(log.cleaner.enabled),如果启动后,每个broker都会启动一个清理管理器线程和多个清理线程,由清理线程负责执行清理任务,他们会选择污浊率(污浊消息占分区总大小的比例)高的分区进行清理。

清理线程会在内存里创建一个map,这个map里面的每个元素都包含了消息键的散列值和偏移量,键的散列值为16B,算上偏移量总共24B。那么如果要清理一个1GB的日志片段,假设每个消息的大小为1KB,那么这个片段就包含了一百万个消息,我们只需要用24MB的map就可以清理这个片段,非常高效。

大概的工作原理

  1. 清理线程在创建好偏移量map后,开始从最干净的片段读取消息,也就是最老的消息开始,把他们的内容和map里面的内容进行比较。
  2. 检查消息的键是否存在与map中,如果不存在,代表消息的值是最新的,就会把消息复制到替换的片段上。若存在,消息会被忽略。
  3. 复制完所有的消息后,我们将替换片段与原始片段进行交换,然后开始清理下一个片段。
  4. 完成整个清理过程后,每个键对应一个不同的消息(消息的值是最新的),形象图如下:

在这里插入图片描述


以上可以小总结,我们只会为每个键保留一个最近的消息。

问题1:但是如果需要删除某个特定键所对应的所有消息的时候,该怎么办。(比如某个用户不再使用某服务,需要删除该客户的所有消息)

答:
1.为了彻底把一个键从系统里删除,应用程序必须发送一个包含该键且值为nul的消息。
2.清理线程发现该消息时,会先进行常规的清理,只保留值为null的消息。而值为null的消息,我们叫它为墓碑消息,会保留一段时间。
3.在这期间,消费者是可以看到这个墓碑消息的,但是发现他的值已经被删除了。
4.这个时间段后,清理线程会移除这个墓碑消息,这个对应的键呢也会从kafka分区当中删除。

问题2:为什么墓碑消息会保留一小段时间?

答:1.首先这个保留时间是可以配置的。
2.其次,配置这个保留时间,是为了让消费者在这个期间内看到这个墓碑消息,让消费者明白,这个消息的值被删除了,专业消费者才会明白:哦这个消息已经没用了,需要把相关的消息都从数据库当中删除。


总结

本文大概从这么几个方面进行概述:

  1. 集群成员关系以及控制器。
  2. Kafka的复制和请求处理过程。
  3. Kafka的存储和文件格式、清除原理。

接下来的文章准备从Kafka可靠的数据传递方面来讲述。

猜你喜欢

转载自blog.csdn.net/Zong_0915/article/details/109578284
今日推荐