afka依赖于ZooKeeper来保存集群中broker、主题、分区的元数据信息和消费者信息。随着版本的更新(从0.9.0.0开始),这种依赖性正在逐步减轻。在老版本的Kafka中,消费者的群组信息、主题信息、消费分区的偏移量等也是存储在ZooKeeper中。然而,新版本的Controller的加入允许broker直接维护这些信息,从而减少了broker和ZooKeeper之间的联系,提高了系统的健壮性。

本文将简要介绍Kafka中的ZooKeeper和Controller之间的联系。

1. ZooKeeper在Kafka中的作用

Kafka利用ZooKeeper保存相应的元数据信息,包括:broker信息,Kafka集群信息,旧版消费者信息以及消费偏移量信息,主题信息,分区状态信息,分区副本分片方案信息,动态配置信息等。

2. Kafka在ZooKeeper中的节点注册

Kafka在ZooKeeper中注册了多个节点,用于存储各种元数据信息。以下是一些常见的节点及其含义:

- /consumers:旧版消费者启动后会在ZK的该节点下创建一个消费者的节点。

- /brokers/seqid:辅助生成的brokerId,当用户没有配置broker.id时,ZK会自动生成一个全局唯一的id。

- /brokers/topics:每创建一个主题就会在该目录下创建一个与该主题同名的节点。

- /borkers/ids:当Kafka每启动一个KafkaServer时就会在该目录下创建一个名为{broker.id}的子节点。

- /config/topics:存储动态修改主题级别的配置信息。

- /config/clients:存储动态修改客户端级别的配置信息。

- /config/changes:动态修改配置时存储相应的信息。

- /admin/delete_topics:在对主题进行删除操作时保存待删除主题的信息。

- /cluster/id:保存集群id信息。

- /controller:保存控制器对应的brokerId信息等。

- /isr_change_notification:保存Kafka副本ISR列表发生变化时通知的相应路径。

3. 在ZooKeeper中存储的信息

在ZooKeeper中存储了broker、consumer等重要znode信息。每个Kafka节点都会在ZooKeeper中注册该机器的配置信息,注册完的kafka节点的topic信息会存在topics目录下面。

在Kafka的早期版本中,并没有采用Kafka Controller这样一个概念来对分区和副本的状态进行管理,而是依赖于Zookeeper,每个broker都会在Zookeeper上为分区和副本注册大量的监听器(Watcher)。当分区或者副本状态变化时,会唤醒很多不必要的监听器,这种严重依赖于Zookeeper的设计会有脑裂、羊群效应以及造成Zookeeper过载的隐患。

Kafka之Controller作为Apache Kafka的核心组件之一,它的主要作用是在Apache ZooKeeper的帮助下管理和协调整个Kafka集群。集群中任意一台Broker都能充当Controller的角色,但是,在整个集群运行过程中,只能有一个Broker成为Controller。具备控制器身份的broker需要比其他普通的broker多一份职责,具体细节如下:

- 它负责管理整个集群中所有分区和副本的状态。

- 当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本。

- 当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。

- 当使用kafka-topics.sh脚本为某个topic增加分区数量时,同样还是由控制器负责分区的重新分配。

本控制器主要负责监听Zookeeper中的一些特定节点的变化,并据此执行相应的操作。

首先,控制器需要为Zookeeper中的一些特定节点注册监听器,以便在这些节点发生变化时能够及时响应。具体来说,控制器需要为/admin/reassign_partitions节点注册PartitionReassignmentListener,用于处理分区重分配的动作;为/isr_change_notification节点注册IsrChangeNotificetionListener,用于处理ISR集合变更的动作;为/admin/preferred-replica-election节点添加PreferredReplicaElectionListener,用于处理优先副本的选举动作。此外,控制器还需要为/brokers/topics节点添加TopicChangeListener和TopicDeletionListener,分别用于处理topic增减的变化和删除topic的动作;以及为/brokers/ids/节点添加BrokerChangeListener,用于处理broker增减的变化。

接下来,控制器需要从Zookeeper中读取获取当前所有与topic、partition以及broker有关的信息,并根据这些信息进行相应的管理。例如,对于所有topic所对应的Zookeeper中的/brokers/topics/[topic]节点,控制器需要添加PartitionModificationsListener,用来监听topic中的分区分配变化。

此外,控制器还需要启动并管理分区状态机和副本状态机。当这些状态机发生变化时,控制器也需要相应地更新集群的元数据信息。

最后,如果参数auto.leader.rebalance.enable设置为true,则控制器还会开启一个名为“auto-leader-rebalance-task”的定时任务,负责维护分区的优先副本的均衡。

总之,本控制器的主要职能包括监听Zookeeper中特定节点的变化、执行相应的操作以及管理分区状态机和副本状态机等。关于这些功能的具体细节,我们将在后续的文章中进行详细的介绍。

afka控制器是一个重要的组件,它在选举成功后会读取Zookeeper中各个节点的数据来初始化上下文信息(ControllerContext)。同时,控制器还需要管理这些上下文信息,例如为某个topic增加分区。在创建分区的过程中,控制器需要更新上下文信息,并将这些变更信息同步到其他普通的broker节点中。

无论是监听器触发的事件、定时任务触发的事件,还是其他事件(如ControlledShutdown),控制器都需要读取或更新其中的上下文信息。这就涉及到多线程间的同步问题。如果仅使用锁机制实现同步,整体性能将会大打折扣。为了解决这一问题,Kafka的控制器采用了基于事件队列的单线程模型。控制器将每个事件进行封装,然后根据事件发生的先后顺序将其暂存到LinkedBlockingQueue中。接着,使用一个专用的线程(ControllerEventThread)按照FIFO(First Input First Output,先入先出)原则顺序处理各个事件,从而在多线程间维护线程安全,无需使用锁机制。

在当前新版本的设计中,只有Kafka Controller在Zookeeper上注册相应的监听器。其他broker极少需要再监听Zookeeper中的数据变化,从而省去了很多不必要的麻烦。不过,每个broker仍会对/controller节点添加监听器,以便监听此节点的数据变化(参考ZkClient中的IZkDataListener)。

当/controller节点的数据发生变化时,每个broker都会更新自身内存中保存的activeControllerId。如果在数据变更前是控制器且在数据变更后自身的brokerid值与新的activeControllerId值不一致,那么就需要“退位”,关闭相应的资源,如状态机、注销监听器等。有时,控制器可能因异常而下线,导致/controller临时节点被自动删除;也有可能是其他原因将此节点删除了。

当一个/controller节点被删除时,每个broker节点会进行选举。在选举前,如果该broker节点在节点被删除前扮演了控制器的角色,那么它需要先执行一个“退位”的操作。为了触发新一轮的选举,可以采取以下两种方式:

1. 如果有特殊需求,可以手动删除/controller节点。这样一来,所有的broker节点都会重新开始选举过程,以确定新的领导者。

2. 同时,关闭与控制器节点对应的broker以及手动向/controller节点写入新的brokerid所对应的数据也可以触发新一轮的选举。通过这种方式,可以确保在节点被删除后,新的leader能够顺利地接管控制器角色并开始管理其他broker节点。

总之,在/controller节点被删除后,为确保系统的正常运行和领导层的更迭,可以通过执行“退位”操作或手动触发新一轮选举来完成节点的替换。这些策略可以根据实际需求灵活应用,以满足系统在不同场景下的管理要求。