classRequestSendThread(val controllerId: Int, val controllerContext: ControllerContext, val queue: BlockingQueue[QueueItem], val networkClient: NetworkClient, val brokerNode: Node, val config: KafkaConfig, val time: Time, val requestRateAndQueueTimeMetrics: Timer, val stateChangeLogger: StateChangeLogger, name: String) extendsShutdownableThread(name = name) {
RequestSendThread 继承了 kafka 中定义的 ShutdownableThread 类,它的 run 方法中,循环调用了下面的 doWork() 方法,来不断的从队列中读取数据:
var clientResponse: ClientResponse = null try { var isSendSuccessful = false while (isRunning && !isSendSuccessful) {
// if a broker goes down for a long time, then at some point the controller's zookeeper listener will trigger a // removeBroker which will invoke shutdown() on this thread. At that point, we will stop retrying. // 如果 broker 挂了一段时间,那么 controller 的 zookeeper listener 会调用 removeBroker 方法, // 这个方法会调用当前类的 shutdown() 方法,标记为线程关闭状态,此时,我们就终止重试。
// 确保响应的请求类型是 leaderAndIsr, stopReplica, updateMetadata val requestHeader = clientResponse.requestHeader val api = requestHeader.apiKey if (api != ApiKeys.LEADER_AND_ISR && api != ApiKeys.STOP_REPLICA && api != ApiKeys.UPDATE_METADATA) thrownewKafkaException(s"Unexpected apiKey received: $apiKey")
val response = clientResponse.responseBody
// 回调 if (callback != null) { callback(response) } } } catch { case e: Throwable => error(s"Controller $controllerId fails to send a request to broker $brokerNode", e) // If there is any socket error (eg, socket timeout), the connection is no longer usable and needs to be recreated. networkClient.close(brokerNode.idString) } }
sealedtraitControllerEvent{ // preempt() is not executed by `ControllerEventThread` but by the main thread. // 抢占队列之前的事件进行优先处理 defpreempt(): Unit defstate: ControllerState }
caseobjectControllerChangeextendsControllerEvent{ overridedefstate: ControllerState = ControllerState.ControllerChange overridedefpreempt(): Unit = {} }
caseobjectShutdownEventThreadextendsControllerEvent{ overridedefstate: ControllerState = ControllerState.ControllerShutdown overridedefpreempt(): Unit = {} }
caseobjectBrokerChangeextendsControllerEvent{ overridedefstate: ControllerState = ControllerState.BrokerChange overridedefpreempt(): Unit = {} }
// Note: `rateAndTimeMetricName` is based on the case object name by default. Changing a name is a breaking change // unless `rateAndTimeMetricName` is overridden. // 注意上面的 rateAndTimeMetricName 方法,这个指标名称,其实是取得是类名
overridedefdoWork(): Unit = { val dequeued = pollFromEventQueue() dequeued.event match { caseShutdownEventThread => // The shutting down of the thread has been initiated at this point. Ignore this event. case controllerEvent => _state = controllerEvent.state
overridedefhandleCreation(): Unit = eventManager.put(ControllerChange) overridedefhandleDeletion(): Unit = eventManager.put(Reelect) overridedefhandleDataChange(): Unit = eventManager.put(ControllerChange) }
其中有三个方法,都是调用传入的 ControllerEventManager 的 put 方法,这个方法我们上面已经介绍过了,就是向 eventQueue 中新增一条数据,等待 ControllerEventThread 去消费并分发 event。
在上文提到的 ControllerEventThread 线程的 process 方法中,我们可以找到这两个事件的处理器 processControllerChange() 和 processReelect()
/* * We can get here during the initial startup and the handleDeleted ZK callback. Because of the potential race condition, * it's possible that the controller has already been elected when we get here. This check will prevent the following * createEphemeralPath method from getting into an infinite loop if this broker is already the controller. */
// 有两种情况代码会走到这里,1是初始化时,2是 zk 上的 /Controller 被删除时 // 多 broker 的情况下,程序走到这里的时候,controller 可能已经选出来了,就直接退出。 if (activeControllerId != -1) { debug(s"Broker $activeControllerId has been elected as the controller, so stopping the election process.") return }
info(s"${config.brokerId} successfully elected as the controller. Epoch incremented to ${controllerContext.epoch} " + s"and epoch zk version is now ${controllerContext.epochZkVersion}")
if (activeControllerId != -1) debug(s"Broker $activeControllerId was elected as controller instead of broker ${config.brokerId}", e) else warn("A controller has been elected but just resigned, this will result in another round of election", e)
case t: Throwable => error(s"Error while electing or becoming controller on broker ${config.brokerId}. " + s"Trigger controller movement immediately", t)
/** * Registers a given broker in zookeeper as the controller and increments controller epoch. * @param controllerId the id of the broker that is to be registered as the controller. * @return the (updated controller epoch, epoch zkVersion) tuple * @throws ControllerMovedException if fail to create /controller or fail to increment controller epoch. */ defregisterControllerAndIncrementControllerEpoch(controllerId: Int): (Int, Int) = { val timestamp = time.milliseconds()
// Read /controller_epoch to get the current controller epoch and zkVersion, // create /controller_epoch with initial value if not exists // // 从 /controller_epoch 读数据 val (curEpoch, curEpochZkVersion) = getControllerEpoch .map(e => (e._1, e._2.getVersion)) // 如果节点不存在,则创建节点, // 如果创建成功,就会将 epoch 初始化为 0 // 如果创建失败,提示节点已存在,则说明有人抢先了,再读一次数据并返回 .getOrElse(maybeCreateControllerEpochZNode())
// Create /controller and update /controller_epoch atomically // 版本号+1 val newControllerEpoch = curEpoch + 1
// 当前 zk 的数据版本作为基准 val expectedControllerEpochZkVersion = curEpochZkVersion
debug(s"Try to create ${ControllerZNode.path} and increment controller epoch to $newControllerEpoch with expected controller epoch zkVersion $expectedControllerEpochZkVersion")
defcheckControllerAndEpoch(): (Int, Int) = { val curControllerId = getControllerId.getOrElse(thrownewControllerMovedException( s"The ephemeral node at ${ControllerZNode.path} went away while checking whether the controller election succeeds. " + s"Aborting controller startup procedure"))
// 如果 /controller 节点里的 id 就是当前节点的 broker id // 则说明当前节点时 controller if (controllerId == curControllerId) {
// 查询 controller epoch val (epoch, stat) = getControllerEpoch.getOrElse( thrownewIllegalStateException(s"${ControllerEpochZNode.path} existed before but goes away while trying to read it"))
// If the epoch is the same as newControllerEpoch, it is safe to infer that the returned epoch zkVersion // is associated with the current broker during controller election because we already knew that the zk // transaction succeeds based on the controller znode verification. Other rounds of controller // election will result in larger epoch number written in zk. // // epoch 也对得上,可以断定返回的 epoch zkVersion 是和当前 broker 相关联的 // 因为之前通过验证 controller 节点(controller znode)已经确认 ZooKeeper 的事务成功了 // 如果有其他 broker 成为 controller(新的选举轮次),它们在 ZooKeeper 中写入的 epoch 一定会比当前的更大 // // id 对得上,epoch 也对得上,说明选举成功 if (epoch == newControllerEpoch) return (newControllerEpoch, stat.getVersion) } thrownewControllerMovedException("Controller moved to another broker. Aborting controller startup procedure") }
/** * This callback is invoked by the zookeeper leader elector on electing the current broker as the new controller. * It does the following things on the become-controller state change - * 1. Initializes the controller's context object that holds cache objects for current topics, live brokers and * leaders for all existing partitions. * 2. Starts the controller's channel manager * 3. Starts the replica state machine * 4. Starts the partition state machine * If it encounters any unexpected exception/error while becoming controller, it resigns as the current controller. * This ensures another controller election will be triggered and there will always be an actively serving controller * * 这个方法是 broker 选举成功,成为 controller 的时候调用的 * 主要工作是: * 1. 初始化 controller 的 context 对象,这个对象保存了 topics/live brokers/partition leader 等信息 * 2. 启动用于与其他 broker 交互的 ControllerChannelManager * 3. 启动 replica 状态机 * 3. 启动 partition 状态机 * 如果过程中发生了未知的异常,就会辞职并重新选举一个 controller, * 保证总有一个 controller 可用 */ privatedefonControllerFailover(): Unit = {
// before reading source of truth from zookeeper, register the listeners to get broker/topic callbacks // 注册监听子节点变化的 handler val childChangeHandlers = Seq(brokerChangeHandler, topicChangeHandler, topicDeletionHandler, logDirEventNotificationHandler, isrChangeNotificationHandler) childChangeHandlers.foreach(zkClient.registerZNodeChildChangeHandler)
// 注册监听节点变化的 handler val nodeChangeHandlers = Seq(preferredReplicaElectionHandler, partitionReassignmentHandler) nodeChangeHandlers.foreach(zkClient.registerZNodeChangeHandlerAndCheckExistence)
// 初始化 topicDelete 状态机 info("Fetching topic deletions in progress") val (topicsToBeDeleted, topicsIneligibleForDeletion) = fetchTopicDeletionsInProgress() info("Initializing topic deletion manager") topicDeletionManager.init(topicsToBeDeleted, topicsIneligibleForDeletion)
// We need to send UpdateMetadataRequest after the controller context is initialized and before the state machines // are started. The is because brokers need to receive the list of live brokers from UpdateMetadataRequest before // they can process the LeaderAndIsrRequests that are generated by replicaStateMachine.startup() and // partitionStateMachine.startup(). // // 先初始化 controller context,再更新 Metadata,然后在启动副本状态机和分区状态机 // 这样其他 brokers 才能知道哪些 brokers 存在,并响应副本状态机和分区状态机的请求。 info("Sending update metadata request") sendUpdateMetadataRequest(controllerContext.liveOrShuttingDownBrokerIds.toSeq, Set.empty)