带你吃透Kafka的可靠性设计( 五 )


在任意时刻,Kafka 集群中有且仅有一个控制器 。每个 broker 启动时会尝试读取 ZooKeeper 的 /controller 节点的 brokerid 的值,如果 brokerid ≠ -1 , 则表示当前集群已有控制器,broker 就会放弃竞?。蝗绻?淮嬖?/controller 节点,broker 就会尝试创建节点,创建成功的 broker 就会成为控制器,将自己的 ID 赋予 brokerid,而对于创建节点失败的 broker 则会在内存中保存当前控制器的 brokerid 值,这个值标识为 activeControllerId 。
上面是启动 Kafka 集群以及正常情况下添加 broker 情况下的选举过程 。那么当控制器出现故障时,就需要重新选举了 。ZooKeeper 中还有一个与控制器有关的 /controller_epoch 节点,该节点是持久节点,里面存储了一个整型的 controller_epoch 值,初始值是 1 。当控制器发生变化时,controller_epoch 就会加 1 。每个和控制器交互的请求一定会带上 controller_epoch , 当控制器发现请求带上的 controller_epoch 比自己内存的小,那么这个请求则是无效请求;如果请求带上的 controller_epoch 比自己内存的大,说明自己不再是控制器 。由此可见,Kafka 是通过 controller_epoch 来保证控制器的唯一性,进而保证相关操作的一致性 。
这里再扩展一下,说下作为控制器的 broker 多出来的责任:

  • 监听分区相关的变化
– 在 ZooKeeper 的 /admin/reassign_partitions 节点注册 PartitionReassignmentHandler,用来处理分区重分配的动作 。
– 在 ZooKeeper 的 /isr_change_notification 节点注册 IsrChangeNotificetionHandler,用来处理 ISR 集合变更的动作 。
– 在 ZooKeeper 的 /admin/preferred-replica-election 节点注册 PreferredReplicaElectionHandler,用来处理优先副本的选举动作 。
  • 监听主题相关的变化
– 在 ZooKeeper 的 /brokers/topics 节点注册 TopicChangeHandler,用来处理主题增减的变化 。
– 在 ZooKeeper 的 /admin/delete_topics 节点注册 TopicDeletionHandler,用来处理删除主题的动作 。
  • 监听 broker 相关的变化
 在 ZooKeeper 的 /brokers/ids 节点注册 BrokerChangeHandler,用来处理 broker 增减的变化 。
  • 从 ZooKeeper 中读取当前所有与主题、分区及 broker 有关的信息并进行相应的管理
 对所有主题对应的在 ZooKeeper 中的 /brokers/topics/ 节点添加 PartitionModificationsHandler,用来监听主题中的分区分配变化 。
  • 启动并管理分区状态机和副本状态机
  • 更新集群的元数据信息
  • 如果设置了 auto.leader.rebalance.enable = true , 则还会开启一个名为“auto-leader-rebalance-task”的定时任务来负责维护分区的优先副本的均衡
成功竞选控制器的 broker 会在当选后,读取包括不限于上面提到的在 ZooKeeper 中的节点的数据 , 初始化上下文信息,并且进行管理 。在 Kafka 中,因为会有大量需要读取或者更新上下文信息的操作 , 所以这里会有多线程问题 。如果单纯采用锁机制实现,那么整体性能会大打折扣 。因此 , 控制器采用的是单线程基于事件队列的模型 。将所有相关的操作、事件进一步封装成一个个事件,按照事件发生的顺序存入 LinkedBlockingQueue 中,最后再使用一个专用线程按 FIFO 的原则处理各个事件 。
控制器、非控制器 broker、ZooKeeper 的关系图如下:
带你吃透Kafka的可靠性设计

文章插图





带你吃透Kafka的可靠性设计

文章插图
只有控制器会注册相应的监听器关注节点的数据变化,其他 broker 则不关注这些节点的数据变化(除了 /controller) 。因为所有 broker 都会关心当前的控制器到底是谁,当 /controller 的数据发生变化时 , 就要更新自己内存中的 activeControllerId 。如果原来是控制器的 broker,发现自己现在不是了,就需要关闭资源 , 如注销只有控制器才需要的监听器等 。不管什么原因造成 /controller 的 brokerid 变更,再重新选举控制器之前,要先确定参选的 broker 里面是否有前控制器,如果有,就要先“退位”,再开始新的选举 。
优点:只有控制器注册监听器 , 可以有效避免严重依赖 ZooKeeper 的设计的通病——脑裂、羊群效应、ZooKeeper 过载 。5 日志同步机制在多副本的设计中,要实现数据一致性和顺序性 , 最简单有效的办法就是选举 leader,由 leader 负责写入顺序,follower 复制同步即可 。只要 leader 不出问题,如宕机、脑裂等,那么就不需要担心 follower 的数据同步问题 。


推荐阅读