Kafka-Controller角色需要做什么?

一、上下文

Kafka-Controller选举》博客中分析了Controller是如何选举出来的,且比如会执行onControllerFailover()。接下来让我们看看Controller角色都承担了哪些职责。

二、注册监听器

在从zookeeper读取资源前,注册监听器以获取 broker/topic 的回调。

    val childChangeHandlers = Seq(brokerChangeHandler, topicChangeHandler, topicDeletionHandler, logDirEventNotificationHandler,
      isrChangeNotificationHandler)
    //依次注册这些 Handler
    //子节点变化监听
    childChangeHandlers.foreach(zkClient.registerZNodeChildChangeHandler)

    val nodeChangeHandlers = Seq(preferredReplicaElectionHandler, partitionReassignmentHandler)
    //节点变化监听
    nodeChangeHandlers.foreach(zkClient.registerZNodeChangeHandlerAndCheckExistence)

三、初始化ControllerContext

1、获取所有的broker

其实就是获取brokers/ids/ 目录下的id来得到broker列表

val curBrokerAndEpochs = zkClient.getAllBrokerAndEpochsInCluster
  def getAllBrokerAndEpochsInCluster: Map[Broker, Long] = {
    //从 brokers/ids 目录下获取所有 brokerid  且排好序
    val brokerIds = getSortedBrokerList
    //为每个 brokerid 都封装一个 请求
    val getDataRequests = brokerIds.map(brokerId => GetDataRequest(BrokerIdZNode.path(brokerId), ctx = Some(brokerId)))
    val getDataResponses = retryRequestsUntilConnected(getDataRequests)
    getDataResponses.flatMap { getDataResponse =>
      val brokerId = getDataResponse.ctx.get.asInstanceOf[Int]
      getDataResponse.resultCode match {
        case Code.OK =>
          // decode 解读 将json 合 brokerid 构建成 BrokerInfo
          //{
          //    "version":5,
          //    "host":"localhost",
          //    "port":9092,
          //    "jmx_port":9999,
          //    "timestamp":"2233345666",
          //    "endpoints":["CLIENT://host1:9092", "REPLICATION://host1:9093"],
          //    *"rack":"dc1",
          //    "features": {"feature": {"min_version":1, "first_active_version":2, "max_version":3}}
          //   }
          Some((BrokerIdZNode.decode(brokerId, getDataResponse.data).broker, getDataResponse.stat.getCzxid))
        case Code.NONODE => None
        case _ => throw getDataResponse.resultException.get
      }
    }.toMap
  }

这一步会得到一个Map[Broker, Long]

Broker中有brokerid,也有这台broker的连接信息、机架信息,此时Controller已经知道自己需要管理的broker有哪些,且可以建立通信

2、判断这些broker是否兼容

val (compatibleBrokerAndEpochs, incompatibleBrokerAndEpochs) = partitionOnFeatureCompatibility(curBrokerAndEpochs)

返回的结果中:

compatibleBrokerAndEpochs 为兼容的borker map

incompatibleBrokerAndEpochs 为不兼容的borker map

那么怎么判断一个broker是否兼容呢?我们看看下面的代码:

  private def partitionOnFeatureCompatibility(brokersAndEpochs: Map[Broker, Long]): (Map[Broker, Long], Map[Broker, Long]) = {
    //partition 方法:
    //一对元素,首先,所有满足谓词p的元素,其次,所有不满足谓词p的元素。
    //这两个可迭代集合分别对应filter和filterNot的结果。
    //这里提供的默认实现需要遍历该集合两次。严格集合在StrictOptimizedIterableOps中有一个重写版本的分区,只需要一次遍历。
    brokersAndEpochs.partition {
      case (broker, _) =>
        !config.isFeatureVersioningSupported ||
        !featureCache.getFeatureOption.exists(
          latestFinalizedFeatures =>
            BrokerFeatures.hasIncompatibleFeatures(broker.features,
              latestFinalizedFeatures.finalizedFeatures().asScala.
                map(kv => (kv._1, kv._2.toShort)).toMap))
    }
  }
def isFeatureVersioningSupported = interBrokerProtocolVersion.isFeatureVersioningSupported
public enum MetadataVersion {

    IBP_0_8_0(-1, "0.8.0", ""),
    //.....
    IBP_2_7_IV0(-1, "2.7", "IV0"),

    public boolean isFeatureVersioningSupported() {
        return this.isAtLeast(IBP_2_7_IV0);
    }
}

MetadataVersion包含不同的Kafka版本,其中2.7就表示该Kafka集群可以兼容的最小版本。如果某个broker的代码版本低于这个版本,就会判定为不兼容。

3、将兼容的broker设置成live状态

controllerContext.setLiveBrokers(compatibleBrokerAndEpochs)
class ControllerContext extends ControllerChannelContext {

  private val liveBrokers = mutable.Set.empty[Broker]
  private val liveBrokerEpochs = mutable.Map.empty[Int, Long]

  def setLiveBrokers(brokerAndEpochs: Map[Broker, Long]): Unit = {
    clearLiveBrokers()
    addLiveBrokers(brokerAndEpochs)
  }

  def addLiveBrokers(brokerAndEpochs: Map[Broker, Long]): Unit = {
    liveBrokers ++= brokerAndEpochs.keySet
    liveBrokerEpochs ++= brokerAndEpochs.map { case (broker, brokerEpoch) => (broker.id, brokerEpoch) }
  }

}

其实就是在其内部维护了一个map(liveBrokers ),将存活的broker都放入其中。

4、获取所有的topic

cont
你遇到的错误: ``` kafka-storage: error: unrecognized arguments: '--bootstrap-controller' ``` 说明你使用的 `kafka-storage format` 命令中包含了 Kafka 不再支持或根本不存在的参数 `--bootstrap-controller`。这个参数是 **旧版本 Kafka 或试验性功能中的用法**,在当前稳定版 Kafka(如 3.0+)中已经被移除或替换。 --- ### ✅ 正确的格式化命令方式(Kafka 3.0+) 从 Kafka 2.8 开始引入了 KRaft(Kafka Raft Metadata mode),取代 ZooKeeper,使用 `kafka-storage.sh` 工具来格式化日志目录。正确的命令应如下: ```bash bin/kafka-storage.sh format \ --config config/kraft/server.properties \ --cluster-id <your-cluster-unique-id> \ --bootstrap-controller ``` 但注意:`--bootstrap-controller` 参数 **仅在特定版本中存在且已被弃用**。在较新版本中(如 Kafka 3.6+),该参数被替换为显式指定控制器角色的方式。 --- ### 🔧 正确法(以 Kafka 3.7 为例) #### 1. 生成唯一的 Cluster ID ```bash # 生成 cluster-id(格式为 base64 编码的 UUID) bin/kafka-storage.sh random-uuid > config/kraft/server.properties.cluster-id ``` 然后复制内容作为你的 cluster-id,例如: ```bash echo "X8aZvMfEShGjYFj9uW5N-Q" > config/kraft/cluster-id.txt ``` #### 2. 配置 server.properties(config/kraft/server.properties) 确保配置文件包含以下关键项: ```properties process.roles=broker,controller node.id=1 controller.quorum.voters=1@localhost:9093 log.dirs=/tmp/kafka-logs listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093 inter.broker.listener.name=PLAINTEXT controller.listener.names=CONTROLLER listener.security.protocol.map=PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT security.inter.broker.protocol=PLAINTEXT ``` #### 3. 执行格式化命令(✅ 正确语法) ```bash bin/kafka-storage.sh format \ --config config/kraft/server.properties \ --cluster-id X8aZvMfEShGjYFj9uW5N-Q \ --ignore-formatted ``` > ⚠️ 注意: > - `--bootstrap-controller` 已被移除。 > - 使用 `--cluster-id` 指定预先生成的集群 ID。 > - `--ignore-formatted` 允许重复运行(避免报错已格式化)。 > - 如果你是多节点集群,需确保所有 controller 的 `controller.quorum.voters` 设置一致。 --- ### 📁 格式化后会发生什么? - 在 `log.dirs` 目录下(如 `/tmp/kafka-logs`)会生成一个 `meta.properties` 文件。 - 内容类似: ```properties version=1 cluster.id=X8aZvMfEShGjYFj9uW5N-Q node.id=1 ``` 这是 Kafka KRaft 模式识别节点归属的核心元数据。 --- ### ❌ 错误原因解释 > `unrecognized arguments: '--bootstrap-controller'` 这是因为你在调用 `kafka-storage format` 时传入了一个不再支持的标志。可能的原因包括: - 参考了过时的教程(Kafka 2.8~3.0 早期测试版文档) - 使用了开发版命令行参数 - 版本不匹配(比如脚本是新版,文档是旧版) --- ### ✅ 如何确认正确参数? 查看帮助文档: ```bash bin/kafka-storage.sh format --help ``` 输出示例(Kafka 3.7): ``` usage: kafka-storage format [-h] --config CONFIG --cluster-id CLUSTER_ID [--add-scram ADD_SCRAM] [--ignore-formatted] [--release-version RELEASE_VERSION] [--feature FEATURE] [--standalone | --no-initial-controllers | --initial-controllers INITIAL_CONTROLLERS] ``` 可以看到没有 `--bootstrap-controller` 参数! --- ### 💡 推荐流程总结 ```bash # 1. 生成 cluster-id bin/kafka-storage.sh random-uuid > /tmp/cluster-id.txt CLUSTER_ID=$(cat /tmp/cluster-id.txt) # 2. 编辑 server.properties 并设置 node.id 和 controller.quorum.voters # 3. 格式化存储 bin/kafka-storage.sh format \ --config config/kraft/server.properties \ --cluster-id $CLUSTER_ID \ --ignore-formatted ``` --- ### ✅ 启动服务 格式化完成后启动 Kafka Server: ```bash bin/kafka-server-start.sh config/kraft/server.properties ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值