欢迎光临
我们一直在努力

庖丁解牛:Kafka 中 ZooKeeper 的核心作用与依赖剖析

庖丁解牛:Kafka 中 ZooKeeper 的核心作用与依赖剖析

    • 1. 引言:Kafka 与 ZooKeeper 的“黄金搭档”时代
    • 2. 全景架构:ZooKeeper 在 Kafka 中的位置
    • 3. ZooKeeper 的六大核心作用
      • 作用一:Broker 注册与发现 —— 动态感知节点存活
      • 作用二:Topic 配置管理 —— 存储元数据
      • 作用三:Controller 选举 —— 集群的“总指挥”如何产生
      • 作用四:集群元数据管理 —— 全局配置的“注册中心”
      • 作用五:Consumer Group 协调 —— 位移管理(旧版 Kafka)
      • 作用六:故障检测与通知 —— 基于 Watch 的事件机制
    • 4. 完整数据流:创建 Topic 时 ZooKeeper 做了什么?
    • 5. ZooKeeper 模式下 Kafka 的依赖与问题
      • 5.1 依赖关系图
      • 5.2 ZooKeeper 模式的已知痛点
    • 6. 对比表:ZooKeeper 依赖 vs. 自研 KRaft
    • 7. 实践:如何查看 Kafka 在 ZooKeeper 中存储的数据?
      • 7.1 连接 ZooKeeper
      • 7.2 关键节点速查表
    • 8. 总结:ZooKeeper 的功与过

🌺The Begin🌺点点关注,收藏不迷路🌺

在 KRaft 全面普及前,ZooKeeper 曾是 Kafka 不可或缺的“大脑”

1. 引言:Kafka 与 ZooKeeper 的“黄金搭档”时代

如果你维护过 Kafka 2.x 及更早版本的集群,一定会发现一个“奇怪”的现象:启动 Kafka 之前,必须先启动 ZooKeeper。

为什么一个消息队列系统要依赖一个独立的分布式协调服务?ZooKeeper 到底为 Kafka 承担了哪些不可替代的职责?

本文将深入剖析 ZooKeeper 在 Kafka 中的六大核心作用,并解释为什么 Kafka 最终决定“抛弃”它——了解这些,才能理解 KRaft 架构的演进逻辑。

2. 全景架构:ZooKeeper 在 Kafka 中的位置

#mermaid-svg-FZKrYGbdWseoYLfx{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FZKrYGbdWseoYLfx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FZKrYGbdWseoYLfx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FZKrYGbdWseoYLfx .error-icon{fill:#552222;}#mermaid-svg-FZKrYGbdWseoYLfx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FZKrYGbdWseoYLfx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FZKrYGbdWseoYLfx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FZKrYGbdWseoYLfx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FZKrYGbdWseoYLfx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FZKrYGbdWseoYLfx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FZKrYGbdWseoYLfx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FZKrYGbdWseoYLfx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FZKrYGbdWseoYLfx .marker.cross{stroke:#333333;}#mermaid-svg-FZKrYGbdWseoYLfx svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FZKrYGbdWseoYLfx p{margin:0;}#mermaid-svg-FZKrYGbdWseoYLfx .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-FZKrYGbdWseoYLfx .cluster-label text{fill:#333;}#mermaid-svg-FZKrYGbdWseoYLfx .cluster-label span{color:#333;}#mermaid-svg-FZKrYGbdWseoYLfx .cluster-label span p{background-color:transparent;}#mermaid-svg-FZKrYGbdWseoYLfx .label text,#mermaid-svg-FZKrYGbdWseoYLfx span{fill:#333;color:#333;}#mermaid-svg-FZKrYGbdWseoYLfx .node rect,#mermaid-svg-FZKrYGbdWseoYLfx .node circle,#mermaid-svg-FZKrYGbdWseoYLfx .node ellipse,#mermaid-svg-FZKrYGbdWseoYLfx .node polygon,#mermaid-svg-FZKrYGbdWseoYLfx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FZKrYGbdWseoYLfx .rough-node .label text,#mermaid-svg-FZKrYGbdWseoYLfx .node .label text,#mermaid-svg-FZKrYGbdWseoYLfx .image-shape .label,#mermaid-svg-FZKrYGbdWseoYLfx .icon-shape .label{text-anchor:middle;}#mermaid-svg-FZKrYGbdWseoYLfx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FZKrYGbdWseoYLfx .rough-node .label,#mermaid-svg-FZKrYGbdWseoYLfx .node .label,#mermaid-svg-FZKrYGbdWseoYLfx .image-shape .label,#mermaid-svg-FZKrYGbdWseoYLfx .icon-shape .label{text-align:center;}#mermaid-svg-FZKrYGbdWseoYLfx .node.clickable{cursor:pointer;}#mermaid-svg-FZKrYGbdWseoYLfx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FZKrYGbdWseoYLfx .arrowheadPath{fill:#333333;}#mermaid-svg-FZKrYGbdWseoYLfx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FZKrYGbdWseoYLfx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FZKrYGbdWseoYLfx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FZKrYGbdWseoYLfx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FZKrYGbdWseoYLfx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FZKrYGbdWseoYLfx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FZKrYGbdWseoYLfx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FZKrYGbdWseoYLfx .cluster text{fill:#333;}#mermaid-svg-FZKrYGbdWseoYLfx .cluster span{color:#333;}#mermaid-svg-FZKrYGbdWseoYLfx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FZKrYGbdWseoYLfx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FZKrYGbdWseoYLfx rect.text{fill:none;stroke-width:0;}#mermaid-svg-FZKrYGbdWseoYLfx .icon-shape,#mermaid-svg-FZKrYGbdWseoYLfx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FZKrYGbdWseoYLfx .icon-shape p,#mermaid-svg-FZKrYGbdWseoYLfx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FZKrYGbdWseoYLfx .icon-shape .label rect,#mermaid-svg-FZKrYGbdWseoYLfx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FZKrYGbdWseoYLfx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FZKrYGbdWseoYLfx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FZKrYGbdWseoYLfx :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

客户端

Kafka Cluster

ZooKeeper Ensemble

元数据读写

注册/心跳

注册/心跳

注册/心跳

Leader 选举

分区状态同步

副本管理

写入

消费

管理操作

ZooKeeper Leader

ZooKeeper Follower

ZooKeeper Follower

Controller Broker

Broker 1

Broker 2

Broker 3

Producer

Consumer

Admin Tool

核心定位:ZooKeeper 是 Kafka 集群的元数据存储中心和分布式协调基石。

3. ZooKeeper 的六大核心作用

作用一:Broker 注册与发现 —— 动态感知节点存活

每个 Broker 启动时,会在 ZooKeeper 的 /brokers/ids 路径下创建一个临时节点:

渲染错误: Mermaid 渲染失败: Parse error on line 2: …LR subgraph ZK路径[/brokers/ids] ———————-^ Expecting 'SEMI', 'NEWLINE', 'SPACE', 'EOF', 'GRAPH', 'DIR', 'subgraph', 'SQS', 'end', 'AMP', 'COLON', 'START_LINK', 'STYLE', 'LINKSTYLE', 'CLASSDEF', 'CLASS', 'CLICK', 'DOWN', 'UP', 'NUM', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'TRAPSTART'

存储内容:每个 Broker 节点的数据包含主机名、端口、机架信息等元数据。

临时节点的妙用:

  • Broker 正常关闭 → 主动删除节点
  • Broker 宕机/网络分区 → ZooKeeper 会话超时 → 自动删除节点

Controller 通过监听 /brokers/ids 的子节点变化,能实时感知 Broker 的上线与下线。

作用二:Topic 配置管理 —— 存储元数据

Topic 的元数据存储在 ZooKeeper 的 /brokers/topics 路径下:

渲染错误: Mermaid 渲染失败: Lexical error on line 2. Unrecognized text. …art TD subgraph “ZK路径: /brokers/topi ———————-^

存储的关键信息:

信息项说明示例
分区数量 Topic 有多少个分区 num_partitions: 6
副本分配 每个分区的副本分布在哪些 Broker replicas: [1,2,3]
Leader 副本 分区的 Leader 在哪个 Broker leader: 1
ISR 列表 与 Leader 保持同步的副本集合 isr: [1,2]
配置覆盖 Topic 级别的特殊配置 min.insync.replicas: 2

作用三:Controller 选举 —— 集群的“总指挥”如何产生

Kafka 集群中必须有一个 Broker 担任 Controller,负责管理分区分配、副本故障转移等任务。

选举机制:

ZooKeeper

Broker 3

Broker 2

Broker 1

ZooKeeper

Broker 3

Broker 2

Broker 1

#mermaid-svg-NbvlrUkIdktTnFbJ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NbvlrUkIdktTnFbJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NbvlrUkIdktTnFbJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NbvlrUkIdktTnFbJ .error-icon{fill:#552222;}#mermaid-svg-NbvlrUkIdktTnFbJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NbvlrUkIdktTnFbJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NbvlrUkIdktTnFbJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NbvlrUkIdktTnFbJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NbvlrUkIdktTnFbJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NbvlrUkIdktTnFbJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NbvlrUkIdktTnFbJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NbvlrUkIdktTnFbJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NbvlrUkIdktTnFbJ .marker.cross{stroke:#333333;}#mermaid-svg-NbvlrUkIdktTnFbJ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NbvlrUkIdktTnFbJ p{margin:0;}#mermaid-svg-NbvlrUkIdktTnFbJ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NbvlrUkIdktTnFbJ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-NbvlrUkIdktTnFbJ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NbvlrUkIdktTnFbJ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-NbvlrUkIdktTnFbJ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-NbvlrUkIdktTnFbJ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-NbvlrUkIdktTnFbJ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-NbvlrUkIdktTnFbJ .sequenceNumber{fill:white;}#mermaid-svg-NbvlrUkIdktTnFbJ #sequencenumber{fill:#333;}#mermaid-svg-NbvlrUkIdktTnFbJ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-NbvlrUkIdktTnFbJ .messageText{fill:#333;stroke:none;}#mermaid-svg-NbvlrUkIdktTnFbJ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NbvlrUkIdktTnFbJ .labelText,#mermaid-svg-NbvlrUkIdktTnFbJ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-NbvlrUkIdktTnFbJ .loopText,#mermaid-svg-NbvlrUkIdktTnFbJ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-NbvlrUkIdktTnFbJ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NbvlrUkIdktTnFbJ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-NbvlrUkIdktTnFbJ .noteText,#mermaid-svg-NbvlrUkIdktTnFbJ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-NbvlrUkIdktTnFbJ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NbvlrUkIdktTnFbJ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NbvlrUkIdktTnFbJ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NbvlrUkIdktTnFbJ .actorPopupMenu{position:absolute;}#mermaid-svg-NbvlrUkIdktTnFbJ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-NbvlrUkIdktTnFbJ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NbvlrUkIdktTnFbJ .actor-man circle,#mermaid-svg-NbvlrUkIdktTnFbJ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-NbvlrUkIdktTnFbJ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

启动或发现 Controller 故障

Controller 在 /controller 节点

记录自己的 brokerId

尝试创建 /controller

尝试创建 /controller

尝试创建 /controller

创建成功 → 成为 Controller

创建失败 → 成为 Follower

创建失败 → 成为 Follower

选举特点:

  • 采用 ZooKeeper 的临时节点 + 原子创建机制实现
  • 最先成功创建 /controller 节点的 Broker 成为 Controller
  • 其他 Broker 监听该节点,当 Controller 宕机时触发重新选举

作用四:集群元数据管理 —— 全局配置的“注册中心”

渲染错误: Mermaid 渲染失败: Lexical error on line 2. Unrecognized text. …art LR subgraph “ZooKeeper 存储的元数据” ———————-^

路径作用
/cluster/id 集群唯一标识,用于跨集群识别
/config/topics Topic 级别的配置覆盖(如 retention.ms)
/config/clients 客户端限流配额配置
/config/users 用户认证和 ACL 权限信息
/admin/reassign_partitions 分区迁移/重分配任务状态

作用五:Consumer Group 协调 —— 位移管理(旧版 Kafka)

⚠️ 重要提示:Kafka 0.9 版本后,Consumer Group 的位移管理已逐步迁移到内部主题 __consumer_offsets。但在 2.x 及更早版本中,部分元数据仍在 ZK 中。

渲染错误: Mermaid 渲染失败: Lexical error on line 2. Unrecognized text. …art TD subgraph “旧版路径 /consumers” ———————-^

旧版 ZK 的职责(Kafka 0.8 及以前):

  • 存储 Consumer Group 的位移 offset
  • 存储消费者与分区的分配关系(Owner)

痛点:ZooKeeper 不擅长高吞吐写入,大规模消费者会压垮 ZK。

作用六:故障检测与通知 —— 基于 Watch 的事件机制

ZooKeeper 的 Watch 机制是 Kafka 实现动态响应的核心:

Broker

ZooKeeper

Controller

Broker

ZooKeeper

Controller

#mermaid-svg-Nqwau0V3QlemL8li{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Nqwau0V3QlemL8li .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Nqwau0V3QlemL8li .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Nqwau0V3QlemL8li .error-icon{fill:#552222;}#mermaid-svg-Nqwau0V3QlemL8li .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Nqwau0V3QlemL8li .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Nqwau0V3QlemL8li .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Nqwau0V3QlemL8li .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Nqwau0V3QlemL8li .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Nqwau0V3QlemL8li .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Nqwau0V3QlemL8li .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Nqwau0V3QlemL8li .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Nqwau0V3QlemL8li .marker.cross{stroke:#333333;}#mermaid-svg-Nqwau0V3QlemL8li svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Nqwau0V3QlemL8li p{margin:0;}#mermaid-svg-Nqwau0V3QlemL8li .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Nqwau0V3QlemL8li text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Nqwau0V3QlemL8li .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Nqwau0V3QlemL8li .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Nqwau0V3QlemL8li .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Nqwau0V3QlemL8li .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Nqwau0V3QlemL8li #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Nqwau0V3QlemL8li .sequenceNumber{fill:white;}#mermaid-svg-Nqwau0V3QlemL8li #sequencenumber{fill:#333;}#mermaid-svg-Nqwau0V3QlemL8li #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Nqwau0V3QlemL8li .messageText{fill:#333;stroke:none;}#mermaid-svg-Nqwau0V3QlemL8li .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Nqwau0V3QlemL8li .labelText,#mermaid-svg-Nqwau0V3QlemL8li .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Nqwau0V3QlemL8li .loopText,#mermaid-svg-Nqwau0V3QlemL8li .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Nqwau0V3QlemL8li .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Nqwau0V3QlemL8li .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Nqwau0V3QlemL8li .noteText,#mermaid-svg-Nqwau0V3QlemL8li .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Nqwau0V3QlemL8li .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Nqwau0V3QlemL8li .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Nqwau0V3QlemL8li .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Nqwau0V3QlemL8li .actorPopupMenu{position:absolute;}#mermaid-svg-Nqwau0V3QlemL8li .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Nqwau0V3QlemL8li .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Nqwau0V3QlemL8li .actor-man circle,#mermaid-svg-Nqwau0V3QlemL8li line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Nqwau0V3QlemL8li :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

loop

[监听子节点变化]

注册 Watch on /brokers/ids

Broker 2 下线

Watch 触发,通知事件

触发故障处理:

重分配分区 Leader

更新 /brokers/topics 元数据

Watch 的应用场景:

监听路径触发事件Kafka 响应动作
/brokers/ids Broker 上线/下线 Controller 更新集群节点列表
/brokers/topics Topic 创建/删除 Controller 分配/回收分区
/controller Controller 变更 新 Controller 加载全量元数据
/admin/reassign_partitions 分区重分配任务 Controller 执行分区迁移

4. 完整数据流:创建 Topic 时 ZooKeeper 做了什么?

Broker 3

Broker 2

Broker 1

Controller

ZooKeeper

Admin/命令行

Broker 3

Broker 2

Broker 1

Controller

ZooKeeper

Admin/命令行

#mermaid-svg-v5gMco5cBdInYAIh{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-v5gMco5cBdInYAIh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-v5gMco5cBdInYAIh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-v5gMco5cBdInYAIh .error-icon{fill:#552222;}#mermaid-svg-v5gMco5cBdInYAIh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-v5gMco5cBdInYAIh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-v5gMco5cBdInYAIh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-v5gMco5cBdInYAIh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-v5gMco5cBdInYAIh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-v5gMco5cBdInYAIh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-v5gMco5cBdInYAIh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-v5gMco5cBdInYAIh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-v5gMco5cBdInYAIh .marker.cross{stroke:#333333;}#mermaid-svg-v5gMco5cBdInYAIh svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-v5gMco5cBdInYAIh p{margin:0;}#mermaid-svg-v5gMco5cBdInYAIh .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-v5gMco5cBdInYAIh text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-v5gMco5cBdInYAIh .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-v5gMco5cBdInYAIh .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-v5gMco5cBdInYAIh .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-v5gMco5cBdInYAIh .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-v5gMco5cBdInYAIh #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-v5gMco5cBdInYAIh .sequenceNumber{fill:white;}#mermaid-svg-v5gMco5cBdInYAIh #sequencenumber{fill:#333;}#mermaid-svg-v5gMco5cBdInYAIh #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-v5gMco5cBdInYAIh .messageText{fill:#333;stroke:none;}#mermaid-svg-v5gMco5cBdInYAIh .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-v5gMco5cBdInYAIh .labelText,#mermaid-svg-v5gMco5cBdInYAIh .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-v5gMco5cBdInYAIh .loopText,#mermaid-svg-v5gMco5cBdInYAIh .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-v5gMco5cBdInYAIh .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-v5gMco5cBdInYAIh .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-v5gMco5cBdInYAIh .noteText,#mermaid-svg-v5gMco5cBdInYAIh .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-v5gMco5cBdInYAIh .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-v5gMco5cBdInYAIh .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-v5gMco5cBdInYAIh .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-v5gMco5cBdInYAIh .actorPopupMenu{position:absolute;}#mermaid-svg-v5gMco5cBdInYAIh .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-v5gMco5cBdInYAIh .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-v5gMco5cBdInYAIh .actor-man circle,#mermaid-svg-v5gMco5cBdInYAIh line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-v5gMco5cBdInYAIh :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

2. 数据持久化

1. 创建 /brokers/topics/my_topic

写入分区分配方案

3. Watch 触发 → 通知 Topic 创建

4. 解析分配方案

5. 创建分区副本

(Leader 角色)

5. 创建分区副本

(Follower 角色)

5. 创建分区副本

(Follower 角色)

6. 确认创建完成

7. 更新 Leader/ISR 信息

8. 创建完成

5. ZooKeeper 模式下 Kafka 的依赖与问题

5.1 依赖关系图

渲染错误: Mermaid 渲染失败: Lexical error on line 2. Unrecognized text. …art TD subgraph “Kafka 功能” K ———————-^

5.2 ZooKeeper 模式的已知痛点

痛点具体表现影响
运维复杂度高 需独立部署和维护 ZK 集群 两个系统的监控、升级、备份各一套
元数据写入瓶颈 ZK 写性能上限约 10k QPS 大规模 Topic 创建/分区重分配慢
分区数量受限 超过 20 万分区后 ZK 压力剧增 限制了 Kafka 集群规模
Controller 恢复慢 故障时需从 ZK 全量加载元数据 故障切换耗时秒级到分钟级
Watch 羊群效应 大量 Broker 同时收到 Watch 通知 瞬时网络风暴
脑裂风险 ZK 与 Kafka 各自独立的选举机制 罕见但存在双 Controller 风险

6. 对比表:ZooKeeper 依赖 vs. 自研 KRaft

维度ZooKeeper 模式KRaft 模式(4.0+)
组件数量 2 套系统 1 套系统
元数据存储 ZooKeeper 树形结构 Kafka 内部主题 __cluster_metadata
一致性协议 ZAB(ZooKeeper) Raft(内置)
Controller 选举 ZK 临时节点 Raft 选举
Broker 注册 ZK 临时节点 Controller 注册
分区数上限 ~20 万 百万级
恢复时间 秒级~分钟级 毫秒级
运维复杂度

7. 实践:如何查看 Kafka 在 ZooKeeper 中存储的数据?

7.1 连接 ZooKeeper

# 连接到 ZK 客户端
zkCli.sh -server localhost:2181

# 查看根路径
ls /
# 输出:[admin, brokers, cluster, config, controller, consumers, isr_change_notification]

# 查看所有 Broker
ls /brokers/ids

# 查看某个 Topic 的分区信息
get /brokers/topics/your_topic

# 查看当前 Controller
get /controller

7.2 关键节点速查表

命令含义
ls /brokers/ids 查看集群所有 Broker
get /controller 查看当前 Controller 是哪个 Broker
ls /brokers/topics 查看所有 Topic
get /config/topics/topic名 查看 Topic 的配置覆盖
ls /admin/reassign_partitions 查看分区重分配任务

8. 总结:ZooKeeper 的功与过

ZooKeeper 的历史贡献:

  • ✅ 为 Kafka 提供了成熟、稳定的分布式协调能力
  • ✅ 降低了 Kafka 初期开发的复杂度
  • ✅ 支撑了 Kafka 成为事实标准的消息队列

ZooKeeper 的局限:

  • ❌ 不适合元数据高吞吐写入场景
  • ❌ 大规模集群下性能瓶颈明显
  • ❌ 双系统运维成本高

演进方向: Kafka 从 2.8 版本开始引入 KRaft 模式,3.3 版本生产可用,4.0 版本彻底移除 ZooKeeper 依赖。这是 Kafka 走向自包含、大规模、高性能的必经之路。


理解 ZooKeeper 在 Kafka 中的作用,不仅是为了运维旧版集群,更是为了深入理解 KRaft 的优化动机。

原创不易,欢迎点赞收藏 💡 评论区聊聊:你还在用 ZooKeeper 模式的 Kafka 吗?

标签:Kafka ZooKeeper 分布式协调 消息队列 架构设计

在这里插入图片描述

🌺The End🌺点点关注,收藏不迷路🌺

赞(0)
未经允许不得转载:171主机测评 » 庖丁解牛:Kafka 中 ZooKeeper 的核心作用与依赖剖析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址