1. Kafka如何复制?
  2. Kafka如何处理来自生产者和消费者的请求?
  3. Kafka的存储细节,比如文件格式和索引?

集群成员关系

Kafka使用Zookeeper来维护成员的信息。每一个broker都有一个唯一的标识符,这个标识符可以在配置文件里指定,也可以自动生成。在broker启动的时候,它通过创建临时节点把自己的ID注册到Zookeeper。Kafka组件订阅Zookeeper的/brokers/ids路径(booker在Zookeeper上的注册路径),当有broker加入集群或退出集群时,这些组件就可以获得通知。

在broker停机、出现网络分区或长时间垃圾回收停顿时,broker会从Zookeeper上断开连接,此时broker在启动时创建的临时节点会自动从Zookeeper上移除。监听broker列表的Kafka组件会被告知broker已移除。

在关闭broker时,它对应的节点也会消失,不过它的ID会继续存在于其他数据结构中。在完全关闭一个broker之后,如果使用相同的ID启动另一个全新的broker,它会立即加入集群,并拥有与旧broker相同的分区和主题。

控制器

Kafka使用Zookeeper的临时节点来选举控制器,并在节点加入集群或退出集群时通知控制器。控制器负责在节点加入或离开集群时进行分区首领选举。控制器使用epoch来避免“脑裂”。“脑裂”是指两个节点同时认为自己是当前的控制器。

6.深入Kafka


复制

Kafka使用主题来组织数据,每个主题被分为若干个分区,每个分区有多个副本。哪些副本被保存在broker上,每个broker可以保存成百上千个属于不同主题和分区的副本。

副本主要有以下两种类型。

首领副本

  • 每个分区都有一个首领副本。为了保证一致性,所有生产者请求和消费这请求都会经过这个副本。

跟随者副本

  • 首领意外的副本都是跟随者副本。跟随者副本不处理来自客户端的请求,他们唯一的任务就是从首领那里复制消息,保持与首领一致的状态。如果首领发生崩溃,其中的一个跟随者会被提升为新首领。

首领的另一个任务是搞清楚哪个跟随者的状态与自己是一致的。

跟随者为了与首领保持同步,跟随者向首领发送获取数据的请求,这种请求与消费者为了读取消息而发送的请求是一样的。首领将响应消息发给跟随者。请求消息里包含了跟随者想要获取消息的偏移量,而且这些偏移量总是有序的。

一个跟随者副本按照请求-响应-请求的方式依次对首领副本发起获取消息请求。首领副本通过跟随者请求的最新偏移量知道每个跟随者的复制进度。通过保存每个跟随者的请求间隔是否超过默认时间10s,来判断是否是同步的。

在跟随者副本中持续请求得到的最新消息副本称为同步的副本。在首领发生失效时,只有同步副本才有可能被选为新首领。

除了当前首领之外,每个分区都有一个首选首领---创建主题时选定的首领就是分区的首选首领。之所以把它叫做首选首领,是因为在创建分区时,需要在broker之间均衡首领。因此,我们希望首选首领在称为真正首领时,broker间的负载最终会得到均衡。默认情况下,Kafka的auto.leader.rebalance.enable被设置为true,它会检查首选首领是不是当前首领,如果不是,并且该副本同步的,那么就会触发首领选举,让首选首领称为当前首领。

处理请求

生产者请求

在生产者请求将消息写入broker时,有个配置参数acks这个配置参数指定了要多少个broker确认才可以认为一个消息写如是成功的。不同的配置对“写入成功”的界定不一样的。如果acks=1,那么只要首领收到消息就认为写入成功;如果acks=all,那么需要所有同步副本收到消息菜算写入成功;如果acks=0,生产者在把消息发出去之后,就完全不需要等待broker的响应。

在消息被写入分区的首领之后,broker开始检查acks配置参数---如果acks被设置为0或者1,那么broker立即返回响应;如果acks被设为all,那么请求会被保存在一个叫作炼狱的缓冲区古里,直到首领发现所有跟随者副本都复制了消息,响应才会被发挥给客户端。

获取请求

broker处理获取请求的方式与处理生产请求的方式很相似,客户端发送请求,想broker请求主题分区里具有特定偏移量的消息。客户端还可以指定broker最多可以从一个分区里返回多少数据。这个现实非常重要,因为客户端需要为broker返回的数据分配足够的内存。如果没有这个限制,broker返回的大量数据有可能好近客户端的内存。

请求最先到达指定的分区首领上,然后客户端通过查询元数据来确保请求的路由是正确的。首领在收到请求时,它会先检查请求是否有效。如,指定的偏移量是在分区上是否存在。

如果请求的偏移量存在,broker将按照客户端指定的数量上限从分区里读取消息,再把消息返回给客户端。Kafka使用零复制技术想客户端发送消息----也就是说,Kafka直接把消息从文件里发送到网络通道,而不需要经过任何中间缓冲区。这是Kafka与其他大部分数据库系统不一样的地方,其他数据库在讲数据发送给客户端之前会先把它们保存在本地缓存里。这项技术避免了字节复制,也不需要管理内存缓冲区,从而获得更好的性能。

客户端除了可以设置broker返回数据的上限,也可以设置下限。在主题消息不是很大的情况下,这样可以减少CPU和网络开销。客户端发送一个请求,broker等到有足够的数据时才把它们返回给客户端,然后客户端再发出请求,而不是让客户端每隔几秒就发送一次请求。


6.深入Kafka


并不是所有保存在分区首领上的数据都可以被哭护短读取。大部分客户端只能读取已经被写入所有同步副本的消息。分区首领知道每个消息会被复制到哪个副本上,在消息还没有被写入所有同步副本之前,是不会发送给消费者的----尝试获取这些消息的请求会得到空的响应而不是错误。

因为还没有被足够多的副本复制的消息被认为是“不安全”的---如果是后领发生崩溃,另一个副本会成为新首领,那么这些消息就丢失了。如果我们允许消费者读取这些消息,可能就会破坏一致性。


6.深入Kafka


分区分配

在创建主题时,Kafka首先会决定如何在broker间分配分区。假设你有6个broker,打算创建一个包含10个分区的主题,并且复制系数为3。那么kafka就会有30个分区副本,它们可以被分配6个broker。在进行分区分配时,我们要达到如下的目标:

  • 在broker间平均地分布分区副本。对于我们的例子来说,就是要保证每个broker可以分到5个副本。
  • 确保每个分区的每个副本分布在不同的broker上。假设分区0的首领副本在broker2上,那么可以把跟随者副本放在broker3和broker4上,但不能放在broker2上,也不能两个都放在broker3上。
  • 如果为broker指定了机架信息,那么尽可能把每个分区的副本分配到不同机架的broker上。这样做是为了保证一个机架的不可用不会导致整体的分区不可用。

相关文章:

  • 2021-04-19
  • 2020-04-01
  • 2022-12-23
  • 2021-04-12
  • 2021-12-26
  • 2021-11-02
  • 2021-07-20
猜你喜欢
  • 2022-01-15
  • 2021-05-04
  • 2021-05-30
  • 2021-09-09
  • 2021-12-20
相关资源
相似解决方案