Controller 发送更新元数据请求,Broker 接收请求并更新元数据流程图解
1.Controller 发送更新元数据请求的场景
在前面几篇关于 Controller 功能的介绍中,多次提到了 Controller 向集群的 Broker 发送更新元数据的请求,如:
①调用 onControllerFailover 方法执行成为 Controller 的逻辑时,向集群中所有运行中的 Broker 发送更新元数据的请求
//向集群中所有运行中的Broker发送更新元数据的请求sendUpdateMetadataRequest(controllerContext.liveOrShuttingDownBrokerIds.toSeq, Set.empty)
②调用 KafkaController.processBrokerChange 方法处理节点数量变更时:
if (newBrokerIds.nonEmpty) {
controllerContext.addLiveBrokersAndEpochs(newBrokerAndEpochs) //执行新加入节点启动的逻辑 onBrokerStartup(newBrokerIdsSorted)}
在 onBrokerStartup 方法中,分别向集群现有的所有 Broker 和新加入的 Broker 发送更新元数据的请求
// 第2步:给集群现有Broker发送元数据更新请求,令它们感知到新增加了BrokersendUpdateMetadataRequest(existingBrokers.toSeq, Set.empty)// 第3步:给新增Broker发送元数据更新请求,令它们同步集群当前的所有分区的元数据sendUpdateMetadataRequest(newBrokers, controllerContext.partitionLeadershipInfo.keySet)
③调用 KafkaController.processBrokerModification 方法处理节点信息变更时:
// 第3步:如果从zk获取到的和元数据中的不相等,说明Broker数据发生了变更// 那么,更新元数据缓存,以及执行onBrokerUpdate方法处理Broker更新的逻辑if (newMetadata.endPoints != oldMetadata.endPoints) {
info(s"Updated broker metadata: $oldMetadata -> $newMetadata") controllerContext.updateBrokerMetadata(oldMetadata, newMetadata) onBrokerUpdate(brokerId)}
在 onBrokerUpdate 方法中,向集群的所有 Broker 发送更新元数据的请求
// 给集群所有Broker发送UpdateMetadataRequest,让它们去更新元数据sendUpdateMetadataRequest(controllerContext.liveOrShuttingDownBrokerIds.toSeq, Set.empty)
④调用 KafkaController.processTopicChange 方法处理新增主题时:
// 第9步:调整新增主题所有分区以及所属所有副本的运行状态为“在线”状态if (addedPartitionReplicaAssignment.nonEmpty) onNewPartitionCreation(addedPartitionReplicaAssignment.keySet)
在 onNewPartitionCreation 方法中,分别使用分区状态机和副本状态机进行分区及副本状态的转换,调用的 handleStateChanges 方法中,都执行了如下代码:
//Controller控制类请求给对应的Broker,通知其状态变化controllerBrokerRequestBatch.sendRequestsToBrokers(controllerContext.epoch)
sendRequestsToBrokers:发送各种控制类请求
def sendRequestsToBrokers(controllerEpoch: Int): Unit = {
try {
val stateChangeLog = stateChangeLogger.withControllerEpoch(controllerEpoch) //发送更新Leader副本和ISR列表的请求 sendLeaderAndIsrRequest(controllerEpoch, stateChangeLog) //发送更新元数据的请求 sendUpdateMetadataRequests(controllerEpoch, stateChangeLog) //发送副本下线的请求 sendStopReplicaRequests(controllerEpoch) } catch {
...}
⑤调用 KafkaController.processTopicDeletion 方法处理删除主题时,待删除主题最后交给了 TopicDeletionManager 管理,其重启主题删除的方法 resumeDeletions 中,无论调用的 completeDeleteTopic 方法还是调用的 onTopicDeletion 方法,都会向集群的 Broker 发送更新元数据的请求:
completeDeleteTopic:该方法通过调用副本状态机的 handleStateChanges 方法,向 Broker 发送更新元数据的请求,原理同上
//第3步:利用副本状态机将这些副本对象转换成NonExistentReplica状态。replicaStateMachine.handleStateChanges(replicasForDeletedTopic.toSeq, NonExistentReplica)
onTopicDeletion:
//第四步:向集群Broker发送指定分区的元数据更新请求client.sendMetadataUpdate(partitions)
综上:可以认为只要 Controller 修改了集群的元数据,就会向 Broker 发送更新元数据的请求。
2. Controller 发送更新元数据请求的流程 在《 深入理解Kafka服务端之Controller基于事件队列的处理流程 》中,分析过 ControllerChannelManager 组件,其管理了 Controller 和集群所有 Broker 之间的网络连接,并为每个 Broker 创建了一个请求队列和一个专属的发送线程,用来向 Broker 发送请求并接收响应。其实,在 Controller 和 ControllerChannelManager 之前还有一层,可以理解为请求批次对象。相关的类如下:
AbstractControllerBrokerRequestBatch:请求批次的抽象基类
ControllerBrokerRequestBatch:请求批次的具体实现类
AbstractControllerBrokerRequestBatch 的定义:
abstract class AbstractControllerBrokerRequestBatch(config: KafkaConfig,//配置类 controllerContext: ControllerContext,//元数据类 stateChangeLogger: StateChangeLogger//日志对象 ) extends Logging {
//Controller 所在节点id val controllerId: Int = config.brokerId //存储LeaderAndIsrRequest请求的集合,key是目标Broker,value是一个map,value.key是主题分区,value.value是待更新的分区信息 val leaderAndIsrRequestMap = mutable.Map.empty[Int, mutable.Map[TopicPartition, LeaderAndIsrRequest.PartitionState]] //存储StopReplicaRequest请求的集合,key是目标Broker,value是待停止副本的信息 val stopReplicaRequestMap = mutable.Map.empty[Int, ListBuffer[StopReplicaRequestInfo]] //存储UpdateMetadataRequest请求的目标节点 val updateMetadataRequestBrokerSet = mutable.Set.empty[Int] //存