首页
学习
活动
专区
圈层
工具
发布

Kafka为什么要抛弃 Zookeeper?

Kafka 集群最烦人的告警,不是 Broker 挂了。

Broker 挂了还能切,分区还能迁,消费者顶多抖一下。真正让人心里发毛的是这种:

ControllerMovedException

TimeoutException: Timed out waiting for a node assignment

Metadata update failed

这类问题第一眼我一般不先看业务代码,先看控制面。Kafka 以前把这块交给 ZooKeeper:Broker 注册、Controller 选举、Topic 元数据、ISR 变化,都绕不开它。

问题也在这里。

Kafka 越长越大,ZooKeeper 就越像一根外挂的骨头。不是 ZooKeeper 不行,而是它不懂 Kafka。

Apache Kafka 4.0 已经只支持 KRaft 模式,ZooKeeper 模式被移除;老集群要升到 4.0 及以上,必须先迁到 KRaft。这个变化不是小修小补,是 Kafka 把自己的控制面收回来了。

以前的结构大概是这样:

Producer / Consumer

      |

    Broker

      |

 ZooKeeper

看着清楚,线上一复杂就不清楚了。

创建 Topic,改分区,Broker 上下线,Controller 变更,元数据要在 Broker 和 ZooKeeper 之间来回同步。这里最怕的不是慢一次,而是状态不一致。你在客户端看到一个 Controller,Broker 认为是另一个;ISR 刚变,元数据还没推完,某些请求就开始超时。

这种问题日志往往不好看,像这样:

[Controller id=2] Broker 5 failed

[Controller id=2] Resigned

[Broker id=7] Metadata delta apply timeout

[AdminClient clientId=ops-check] disconnected before response

这地方我第一眼就不太信“网络偶发”。控制面抖了,业务面再健康也白搭。

KRaft 做的事,就是把 ZooKeeper 这一层拿掉,让 Kafka 自己维护元数据日志。Controller 不再是去 ZooKeeper 里抢一个临时节点,而是由一组 Controller 节点组成 quorum,用 Raft 思路复制元数据。

结构变成这样:

Producer / Consumer

      |

    Broker

      |

KRaft Controller Quorum

      |

Metadata Log

这里有个很关键的变化:元数据不再散在外部系统里,而是变成 Kafka 自己的一条日志。

这就舒服多了。Kafka 最擅长什么?写日志、复制日志、按 offset 推进状态。KRaft 等于是把 Kafka 擅长的那套东西,用到了自己的控制面上。

以前运维排查 Kafka,要同时盯两套东西:

Kafka Broker 是否正常

ZooKeeper ensemble 是否正常

Broker 到 ZooKeeper 的 session 是否正常

Controller 是否频繁切换

现在至少少了一半心智负担。不是说 KRaft 不会出问题,而是问题边界清楚了。

代码里也能看出来这个变化。以前有些老系统会直接连 ZooKeeper 去读 Broker 列表,这种代码我现在看到基本会标红:

public final class KafkaClusterProbe {

  private KafkaClusterProbe() {

  }

  public static ClusterView read(String bootstrapServers) {

      Properties props = new Properties();

      props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);

      props.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, "5000");

      props.put(AdminClientConfig.DEFAULT_API_TIMEOUT_MS_CONFIG, "8000");

      try (AdminClient admin = AdminClient.create(props)) {

          DescribeClusterResult result = admin.describeCluster();

          String clusterId = result.clusterId().get();

          Node controller = result.controller().get();

          Collection<Node> nodes = result.nodes().get();

          return new ClusterView(clusterId, controller.id(), nodes.size());

      } catch (Exception e) {

          throw new IllegalStateException("Kafka 控制面探测失败,先别急着重启业务服务", e);

      }

  }

  public record ClusterView(String clusterId, int controllerId, int brokerCount) {

  }

}

这段代码没碰 ZooKeeper,只认bootstrap.servers。这才是新 Kafka 客户端该有的姿势。

Kafka 4.0 之后,命令行里的--zookeeper也被移除了,管理操作统一走--bootstrap-server。这个细节挺能说明问题:Kafka 不希望应用、脚本、运维工具再绕到 ZooKeeper 后门里扒元数据。

抛弃 ZooKeeper,还有一个原因是扩展性。

小集群没感觉。几十个 Topic,几百个分区,ZooKeeper 顶得住。可一旦分区数上来,Controller 重启后要恢复大量元数据,Broker 变化要推一堆通知,ZooKeeper watch 也会跟着热闹。

这类慢不是 SQL 那种慢,可以 explain 一下。它更像控制面卡顿:创建 Topic 慢、分区扩容慢、Leader 选举慢,最后业务侧看到的就是发送超时、消费组抖动、元数据刷新失败。

KRaft 的思路更直接:所有元数据变更先进元数据日志,Controller quorum 复制,Broker 再按日志应用变更。顺序清楚,恢复也清楚。

迁移时我反而不建议一上来就喊“升级 Kafka 4.0”。先查现场:

public class KafkaVersionGate {

  public static void checkBeforeUpgrade(String bootstrapServers) {

      KafkaClusterProbe.ClusterView view = KafkaClusterProbe.read(bootstrapServers);

      if (view.brokerCount() < 3) {

          throw new IllegalStateException("Broker 数量太少,别在这个状态下折腾 KRaft 迁移");

      }

      System.out.println("clusterId=" + view.clusterId());

      System.out.println("controllerId=" + view.controllerId());

      System.out.println("brokerCount=" + view.brokerCount());

  }

}

真正迁移前,我会先看三件事:Controller 有没有频繁切换,分区副本有没有长期 under-replicated,运维脚本里还有没有zookeeper.connect和--zookeeper。

有些老脚本最坑,平时不跑,出事故才跑。一跑发现还在读 ZooKeeper 路径:

/brokers/ids

/controller

/admin/delete_topics

这种东西不清掉,升版本就是给自己埋雷。

所以 Kafka 抛弃 ZooKeeper,不是嫌弃 ZooKeeper 老,也不是为了赶技术潮流。

更直接点说,是 Kafka 的控制面长大了,不能再靠一个外部协调系统撑着。元数据、选主、状态复制,这些东西必须回到 Kafka 自己手里。

以前 Kafka + ZooKeeper 是两套系统一起扛事。现在 KRaft 是 Kafka 自己扛。

架构少一层,排障少一层,升级少一层风险。线上系统里,少一个关键依赖,很多时候就是少一半事故入口。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OFEiLl4FPGxHlMb-kuJRXtLw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券