云计算百科
云计算领域专业知识百科平台

Kafka + ZooKeeper架构基础介绍

Kafka + ZooKeeper架构基础介绍

一、整体架构

ZooKeeper 是 Kafka 2.8 之前的必选协调组件,只做集群管控,不参与消息读写:

  • 存储集群元数据(Broker、Topic、分区、副本、ISR 信息)
  • 提供分布式锁与临时节点,实现 Kafka Controller 选举
  • 感知 Broker 上下线
  • 持久化分区 Leader 状态

Kafka 只负责:消息接收、存储、复制、消费。


二、ZooKeeper 在 Kafka 中的核心作用

1. 存储的关键元数据(ZK 节点路径)

ZK 节点路径作用
/brokers/ids/[brokerId] Broker 注册临时节点,标记存活
/brokers/topics/[topic] 主题的分区数、副本分配方案
/brokers/topics/[topic]/partitions/[N]/state 分区状态:Leader、ISR、副本集合
/controller 保存当前 Controller 信息(临时节点)
/config/topics/[topic] 主题级配置
/admin/delete_topics 待删除主题队列

2. 四大核心职责

  • Broker 注册与健康检测

    • Broker 启动 → 创建临时节点 /brokers/ids/xx
    • 断开连接 → 节点自动删除 → Kafka 感知下线。
  • Kafka Controller 选举

    • 所有 Broker 争抢创建 /controller 临时节点,成功即为 Controller。
  • 分区 Leader 状态持久化

    • 每次 Leader 变更,结果写入 ZooKeeper,保证全局一致。
  • 集群统一视图

    • 所有 Broker 共享同一份元数据,不会出现脑裂。

  • 三、ZooKeeper 配置(供 Kafka 使用)

    zoo.cfg 完整配置

    # 基础时间单位 ms
    tickTime=2000
    initLimit=10
    syncLimit=5

    # 数据目录(生产必须独立磁盘)
    dataDir=/data/zookeeper/data
    dataLogDir=/data/zookeeper/log

    clientPort=2181

    # Kafka 集群需要高连接数
    maxClientCnxns=2000

    # 自动清理快照与日志
    autopurge.snapRetainCount=5
    autopurge.purgeInterval=24

    # ZK 集群(3 节点示例)
    server.1=zk1:2888:3888
    server.2=zk2:2888:3888
    server.3=zk3:2888:3888

    关键配置说明

    • dataDir:ZK 内存数据快照目录
    • dataLogDir:事务日志目录(必须独立磁盘,性能关键)
    • maxClientCnxns:Kafka 集群需调高,避免连接被拒
    • autopurge:防止日志占满磁盘

    节点标识(必须配置)

    echo "1" > /data/zookeeper/data/myid

    myid 必须与 server.1/2/3 中的数字一致。


    四、Kafka 连接 ZooKeeper 核心配置

    server.properties 关键配置

    # 唯一标识
    broker.id=1

    # 监听地址
    listeners=PLAINTEXT://0.0.0.0:9092
    advertised.listeners=PLAINTEXT://192.168.1.101:9092

    # 日志存储
    log.dirs=/data/kafka/logs
    num.partitions=8
    default.replication.factor=3
    min.insync.replicas=2

    # ======================
    # ZK 核心配置
    # ======================
    zookeeper.connect=zk1:2181,zk2:2181,zk3:2181/kafka
    zookeeper.connection.timeout.ms=10000
    zookeeper.session.timeout.ms=18000

    # 生产关闭自动创建主题
    auto.create.topics.enable=false
    delete.topic.enable=true

    重点说明

    • zookeeper.connect 末尾 /kafka 是命名空间,用于多集群隔离
    • zookeeper.session.timeout.ms:超时即认为 Broker 已下线
    • min.insync.replicas:最小同步副本数,保证数据不丢失

    五、Kafka 分区主从机制:Leader / Follower / ISR

    1. 主从角色

    • Leader:每个分区只有一个,负责读写
    • Follower:只从 Leader 同步数据,不提供读写
    • ISR(In-Sync Replicas):与 Leader 保持同步的副本集合

    2. 核心规则

    • 生产者、消费者只与 Leader 交互
    • Follower 异步拉取消息
    • 只有 ISR 内的副本才有资格被选为新 Leader

    3. 高可用保证

    • Leader 宕机 → 从 ISR 中选新 Leader
    • 配置 acks=all + min.insync.replicas=2 可保证消息不丢失

    六、Kafka Controller 选举流程(基于 ZK)

    1. Controller 是什么?

    集群中唯一的管理节点,负责:

    • 分区 Leader 选举
    • 副本分配与重分配
    • 感知 Broker 上下线
    • 同步元数据给所有 Broker

    2. 选举流程(完全依赖 ZooKeeper)

    ZooKeeperBroker 3Broker 2Broker 1ZooKeeperBroker 3Broker 2Broker 1#mermaid-svg-rRsd8m7GDXMf1Gbt{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-rRsd8m7GDXMf1Gbt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rRsd8m7GDXMf1Gbt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rRsd8m7GDXMf1Gbt .error-icon{fill:#552222;}#mermaid-svg-rRsd8m7GDXMf1Gbt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rRsd8m7GDXMf1Gbt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rRsd8m7GDXMf1Gbt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rRsd8m7GDXMf1Gbt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rRsd8m7GDXMf1Gbt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rRsd8m7GDXMf1Gbt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rRsd8m7GDXMf1Gbt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rRsd8m7GDXMf1Gbt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rRsd8m7GDXMf1Gbt .marker.cross{stroke:#333333;}#mermaid-svg-rRsd8m7GDXMf1Gbt svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rRsd8m7GDXMf1Gbt p{margin:0;}#mermaid-svg-rRsd8m7GDXMf1Gbt .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rRsd8m7GDXMf1Gbt text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-rRsd8m7GDXMf1Gbt .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-rRsd8m7GDXMf1Gbt .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-rRsd8m7GDXMf1Gbt .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-rRsd8m7GDXMf1Gbt .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-rRsd8m7GDXMf1Gbt #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-rRsd8m7GDXMf1Gbt .sequenceNumber{fill:white;}#mermaid-svg-rRsd8m7GDXMf1Gbt #sequencenumber{fill:#333;}#mermaid-svg-rRsd8m7GDXMf1Gbt #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-rRsd8m7GDXMf1Gbt .messageText{fill:#333;stroke:none;}#mermaid-svg-rRsd8m7GDXMf1Gbt .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rRsd8m7GDXMf1Gbt .labelText,#mermaid-svg-rRsd8m7GDXMf1Gbt .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-rRsd8m7GDXMf1Gbt .loopText,#mermaid-svg-rRsd8m7GDXMf1Gbt .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-rRsd8m7GDXMf1Gbt .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-rRsd8m7GDXMf1Gbt .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-rRsd8m7GDXMf1Gbt .noteText,#mermaid-svg-rRsd8m7GDXMf1Gbt .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-rRsd8m7GDXMf1Gbt .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rRsd8m7GDXMf1Gbt .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rRsd8m7GDXMf1Gbt .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-rRsd8m7GDXMf1Gbt .actorPopupMenu{position:absolute;}#mermaid-svg-rRsd8m7GDXMf1Gbt .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-rRsd8m7GDXMf1Gbt .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-rRsd8m7GDXMf1Gbt .actor-man circle,#mermaid-svg-rRsd8m7GDXMf1Gbt line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-rRsd8m7GDXMf1Gbt :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}所有 Broker 启动B1 宕机(网络断开)create /controller (ephemeral)成功 → B1 成为 Controllercreate /controller → 失败watch /controllercreate /controller → 失败watch /controllerSession 超时(~6s)删除 /controller 节点Watch 触发create /controller → 成功 → B2 成为新 Controller

    特点:抢占式、简单、强依赖 ZK 临时节点特性。


    七、Kafka 分区 Leader 选举流程

    触发时机

    • Broker 宕机
    • 分区重分配
    • 集群启动/停止

    完整流程

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

    Controller 监听到 Broker 下线

    获取该 Broker 上所有 Leader 分区

    遍历每个分区

    查询该分区的 ISR 列表

    选择第一个存活的副本作为新 Leader

    更新 ZK 中的分区状态

    广播新 Leader到所有 Broker

    客户端开始访问新 Leader

    详细步骤

  • Controller 通过 ZK 监听到 Broker 下线
  • 找到该 Broker 上所有 Leader 分区
  • 对每个分区执行:
    • 读取该分区的 ISR 列表
    • 选择 第一个存活的副本 作为新 Leader
  • 将新状态写入 ZK:
    /brokers/topics/[topic]/partitions/[x]/state
  • Controller 把新元数据广播给所有 Broker
  • 客户端开始访问新 Leader

  • 八、Kafka 主从同步机制

  • Follower 主动向 Leader 发起同步请求
  • Leader 将消息写入后,同步给 Follower
  • Follower 写入成功后回复 ACK
  • 满足同步条件后加入 ISR
  • 生产者 acks=all 时:
    • 必须等 ISR 中所有副本 确认
    • 才返回生产成功

  • 九、常用运维命令(ZK + Kafka)

    1. 查看当前 Controller

    bin/zookeeper-shell.sh zk1:2181 get /controller

    2. 查看主题详情(Leader / ISR)

    bin/kafka-topics.sh –describe –topic test –bootstrap-server kafka1:9092

    3. 查看在线 Broker

    bin/zookeeper-shell.sh zk1:2181 ls /brokers/ids

    4. 查看分区状态

    bin/zookeeper-shell.sh zk1:2181 get /brokers/topics/test/partitions/0/state


    十、关键故障机制

    1. Broker 宕机

    • ZK 会话超时 → /brokers/ids/xx 消失
    • Controller 监听到 → 开始分区 Leader 迁移

    2. Controller 宕机

    • /controller 临时节点消失
    • 所有 Broker 重新选举 Controller
    • 新 Controller 从 ZK 加载全量元数据

    3. ZK 不可用影响

    • 已有的消息读写正常
    • 无法创建/删除 Topic
    • 无法重新选举 Controller、分区 Leader
    • 无法上下线 Broker

    十一、总结

  • ZooKeeper:Kafka 集群的“配置中心 + 协调中心”。
  • Broker 注册:依靠 ZK 临时节点实现存活检测。
  • Controller 选举:争抢 ZK /controller 临时节点实现。
  • 分区主从:Leader 提供读写,Follower 同步,ISR 保证高可用。
  • Leader 选举:由 Controller 从 ISR 中选出,结果存入 ZK。
  • ZK 作用边界:只管集群协调,不管消息收发。

  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » Kafka + ZooKeeper架构基础介绍
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!