乐趣区

Kafka控制器选举原理

1. Kafka 控制器介绍

  在 Kafka 集群中会有一个或多个 broker,其中有一个 broker 会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态。当某个分区的 leader 副本(一个分区会有多个副本,其中只有 leader 副本对外提供读写服务)出现故障时,由控制器负责为该分区选举新的 leader 副本。当检测到某个分区的 ISR 集合发生变化时,由控制器负责通知所有 broker 更新其元数据信息。当为某个 Topic 增加分区数量时,由控制器负责分区的重新分配。
  分区集合介绍:分区中的所有副本统称为 AR(Assigned Replicas)。所有与 leader 副本保持一定程度同步的副本(包括 leader 副本在内)组成 ISR(In-Sync Replicas)。与 leader 副本同步滞后过多的副本(不包括 leader 副本)组成 OSR(Out-of-Sync Replicas)。
  leader 副本负责维护和跟踪 ISR 集合中所有 follower 副本的滞后状态,当 follower 副本落后太多或失效时,leader 副本会把它从 ISR 集合中剔除。当 OSR 集合中有 follower 副本“追上”了 leader 副本,那么 leader 副本会把它从 OSR 集合转移至 ISR 集合。默认情况下,当 leader 副本发生故障时,只有在 ISR 集合中的副本才有资格被选举为新的 leader。

2. Kafka 控制器选举原理

  Kafka 中的控制器选举工作依赖于 Zookeeper,成功竞选成为控制器的 broker 会在 Zookeeper 中创建/controller 临时(Ephemeral)节点,此临时节点的内容参考如下:

{"version":1,"brokerid":0,"timestamp":"1593330804078"}

  其中 version 与 Kafka 版本相关,对同一个 Kafka 版本来说为固定值。brokerid 表示成为控制器的 broker 的 id 编号,timestamp 表示竞选成为控制器时的时间戳(精确到毫秒)。
  在任意时刻,集群中有且只有一个控制器。每个 broker 启动的时候会去尝试读取 /controller 节点的 brokerid 的值,如果读取到的 brokerid 的值不为 -1,表示已经有其他 broker 节点成功竞选为控制器,所以当前 broker 就会放弃竞选;如果 Zookeeper 中不存在 /controller 节点,或者这个节点的数据异常,那么就会尝试去创建 /controller 节点。当前 broker 去创建节点的时候,也有可能有其他 broker 同时去尝试创建这个节点,只有创建成功的那个 broker 才会成为控制器。每个 broker 都会在内存中保存当前控制器的 brokerid 值,这个值可以标识为 activeControllerId。
  Zookeeper 中还有一个与控制器有关的 /controller_epoch 节点,这个节点是 持久(Persistent)节点 ,节点中存放的是一个整型的 controller_epoch 值。controller_epoch 值用于记录控制器发生变更的次数,即记录当前的控制器是第几代控制器,我们也可以称之为“ 控制器纪元 ”。
  controller_epoch 的初始值为 1,即集群中的第一个控制器的纪元为 1,当控制器发生变更时,每选出一个新的控制器就将该字段值加 1。 每个和控制器交互的请求都会携带 controller_epoch 这个字段,如果请求的 controller_epoch 值小于内存中的 controller_epoch 值,则认为这个请求是向已经过期的控制器发送的请求,那么这个请求会被认定为无效的请求。如果请求的 controller_epoch 值大于内存中的 controller_epoch 值,那么说明已经有新的控制器当选了(也就是说接收到这种请求的 broker 已经不再是控制器了)。由此可见,Kafka 通过 controller_epoch 来保证控制器的唯一性,进而保证相关操作的一致性。
  具备控制器身份的 broker 需要比其他普通的 broker 多一份职责,具体细节如下:

  • 监听分区的变化。
  • 监听主题的变化。
  • 监听 broker 相关的变化。
  • 从 Zookeeper 中读取获取当前所有与主题、分区及 broker 有关的信息并进行相应的管理。
  • 启动并管理分区状态机和副本状态机。
  • 更新集群的元数据信息。

  当 /controller 节点的数据发生变化时,每个 broker 都会更新自身内存中保存的 activeControllerId。如果 broker 在数据变更前是控制器,在数据变更后自身的 brokerid 值与新的 activeControllerId 值不一致,那么就需要“退位”,关闭相应的资源,比如关闭状态机、注销相应的监听器等。有可能控制器由于异常而下线,造成 /controller 这个临时节点被自动删除;也有可能是其他原因将此节点删除了。
  当 /controller 节点被删除时,每个 broker 都会进行选举,如果 broker 在节点被删除前是控制器,那么在选举前还需要有一个“退位”的动作。如果有特殊需要,则可以手动删除 /controller 节点来触发新一轮的选举。当然关闭控制器所对应的 broker,以及手动向 /controller 节点写入新的 brokerid 的所对应的数据,同样可以触发新一轮的选举。

3. 总结

  Kafka 控制器选择的流程并不复杂,但是考虑的各种边界条件还是比较周到的,程序的健壮性比较好。有时候我们需要根据业务场景去设计或者调整某种分布式集群,Kafka 控制器的选举也可以是一个很好的借鉴,尤其是 /controller_epoch 的设计,考虑到了控制器可能会因为某种原因过时,保证了控制器的唯一性。

4. 参考文献

《深入理解 Kafka:核心设计与实践原理》

退出移动版