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:核心设计与实践原理》