当前位置: 首页 > news >正文

zk02-知识演进

zk02-知识演进

【尚硅谷】大数据技术之Zookeeper 3.5.7版本教程:https://www.bilibili.com/video/BV1to4y1C7gw


1-知识总结

  • 1)ZK核心设计:【类uninx文件系统】+【观察者模式通知机制】
    • 1)类uninx文件系统->原子性设计
    • 2)观察者模式通知机制->监听器原理
  • 2)ZK核心目的:【注册内容】并感知【内容变化】
    • 1)向【统一入口】进行【内容注册】
    • 2)【统一入口】可实时监听【内容变化】
  • 3)ZK应用场景->由节点动态上下线引发的场景应用
    • 1)统一命名服务、统一配置管理、统一集群管理
    • 2)服务器节点动态上下线、软负载均衡等
  • 4)ZK集群工作机制
    • 1)ZKClient如何保证数据一致性->每个ZKServer都有一份完整的数据
    • 2)ZKServer之间如何保证数据一致性->【数据写入->由Leader广播Server,半数ACK后提交】+【Leader和Server之间数据同步】
    • 3)Leader和Follower如何进行选举

2-知识整理

1-【类uninx文件系统】+【观察者模式通知机制】

1)类 Unix 文件系统 -> 原子性设计
  • Zookeeper 的数据模型确实类似于 Unix 文件系统,是一个层次化的命名空间,每个节点称为 ZNode
  • 每个 ZNode 可以存储数据,也可以有子节点。
  • 原子性体现在:每个操作(如创建、删除、更新)都是原子的,要么成功,要么失败,不会部分执行。
  • 此外,Zookeeper 保证顺序一致性:所有客户端看到的数据更新顺序是一致的。
2)观察者模式通知机制 -> 监听器原理
  • Zookeeper 提供了 Watcher 机制,这确实是观察者模式的实现。
  • 客户端可以在读取某个 ZNode 时设置一个 Watcher,当该 ZNode 发生变化(如数据变更、子节点增减等),Zookeeper 会异步通知客户端。
  • 这种机制使得客户端可以实时感知数据变化,而无需轮询。

🔍 补充说明:

特性说明
Watcher 是一次性的一旦被触发,Watcher 就失效,需要重新注册。
通知是异步的不保证实时性,但通常很快。
Watcher 不携带数据它只告诉你“某个节点变了”,你需要重新读取数据。

✅ 总结一句话:

Zookeeper 的核心设计确实是“类 Unix 文件系统” + “观察者模式的通知机制”,前者提供结构化数据存储和原子操作,后者实现高效的事件驱动通知。


2-数据一致性方法

数据一致性核心2个点:【数据写入->由Leader广播Server,半数ACK后提交】+【Leader和Server之间数据同步】

你的理解已经非常接近核心机制了,但还缺几个关键细节。
ZooKeeper 并不是“简单地把数据写到所有 Server 然后半数 ACK 就完事”,它底层依赖的是 ZAB(ZooKeeper Atomic Broadcast)协议,该协议把“写请求”当成**一条事务日志”来广播,并保证:

  1. 全局唯一顺序
  2. 崩溃恢复后依旧一致
  3. 只有提交过的事务才对客户端可见

下面给你一幅“完整拼图”,你可以直接替换掉原先脑中的那张简化图。


一、写入一条数据时到底发生了什么(正常流程)

  1. 客户端把写请求发给任意一台 Follower/Observer
    (也可以直接连 Leader,但生产环境通常前面挂客户端库+负载均衡)。

  2. 若请求落在 Follower:
    Follower 把请求转发给 Leader → 进入 ZAB 阶段。

  3. Leader 把这条请求包装成一个事务 Proposal

    • 分配全局单调递增的 zxid(64 位,高 32 位 epoch,低 32 位自增)
    • 写本地磁盘日志(redo-log,默认 sync)
    • 把 Proposal 按 zxid 顺序并发广播给所有 Follower
  4. Follower 收到 Proposal 后:

    • 先写本地磁盘日志(sync)
    • 返回一个 ACK(携带本机最大 zxid)
  5. Leader 统计 ACK

    • 含自己在内,“ACK 数 > 集群半数”(≥ quorum)即认为“可以提交”
    • 向所有 Follower 发 COMMIT 消息(带上刚提交的 zxid)
  6. Follower 收到 COMMIT 后:

    • 把该事务应用到内存 DataTree(此时才正式改内存)
    • 之后读请求才能看到新数据
  7. 最初那台 Follower 把响应返回客户端 → 调用返回成功


二、崩溃恢复阶段(Leader 挂了重新选主)

  1. 剩余节点进入 Looking 状态,基于 zxid + myid 投票
  2. 选出的新 Leader 必须拥有已提交事务的最大 zxid(由 ZAB 的 epoch 逻辑保证)
  3. 新 Leader 会先补齐其它 Follower 缺失的提案:
    • 把本地日志中所有 COMMIT 过的事务重新发送给 Follower
    • Follower 把这些事务重放到本地 DataTree
  4. 直到集群中**“过半节点”**的日志完全一致,才开始接受新的写请求
    → 因此客户端永远不会读到“旧主已提交、新主却丢失”的数据

三、读一致性怎么保证

  • ZooKeeper 默认是最终一致性读
    客户端可能连到某台还没及时同步的 Follower,读到的数据略旧。
  • 如果业务必须读到“最新已提交”数据,可以用 sync() + read 组合:
    1. 先对目标路径发一个 sync() 请求——它会强制让当前连接的 Server 从 Leader 拉一下最新 zxid;
    2. 再执行读操作,就能保证读到至少不早于 sync 时刻的已提交数据

四、纠正 / 补充你原句的 3 个细节

你原句需要修正/补充
“每个 ZKServer 都有一份完整的数据”日志+内存 DataTree 是完整的,但 Observer 可以不参与投票,只异步拉取。
“由 Leader 广播 Server,半数 ACK 后提交”准确说法是:Leader 广播 Proposal → 过半 ACK → Leader 发 COMMIT → 各 Server 改内存。单纯“广播数据”不够,必须走 ZAB 的两阶段(Proposal+Commit)。
“Leader 和 Server 之间数据同步”日常写操作靠 ZAB 实时广播;崩溃后靠** Leader 选举 + 差异日志回捕(TRUNC + SNAP + DIFF)** 来对齐,而不是简单的全量同步。

五、一句话总结

ZooKeeper 通过 ZAB 原子广播协议把“写请求”变成顺序一致的事务日志

  • 正常阶段:两阶段提交(Proposal + Commit)+ quorum 确认
  • 崩溃阶段:选含最新已提交 zxid 的节点为新主,再补齐/回滚其它节点
  • 读阶段:默认最终一致,可手动 sync() 拿到线性一致快照

因此,它才能保证 “客户端一旦收到成功响应,数据就永久存在,且后续任何节点崩溃、 Leader 切换都不会丢失这条已提交数据” —— 这正是 ZooKeeper 所说的“单拷贝一致性”(single-system image)。


3-zookeeper监视器原理

下面把 ZooKeeper 的“监视器”(Watcher)机制从** API 、底层实现、网络协议、事件流、典型坑**五个维度彻底拆开讲一遍。看完你就能明白:

  1. 为什么 Watch 只能被触发一次;
  2. 服务端到底存了什么;
  3. 客户端收到事件后数据却可能“看不见”是怎么回事;
  4. 怎么利用“异步回调 + 版本号”做出业务层可靠的监听框架。

一、API 视角:三种调用点、两类事件

  1. 可以挂 Watch 的读接口

    • getData(path, watch?) → 监听“数据变更
    • exists(path, watch?) → 监听“节点创建、删除、数据变更
    • getChildren(path, watch?) → 监听“子节点列表增减
  2. 事件类型(org.apache.zookeeper.Watcher.Event)

    • NodeCreated、NodeDeleted、NodeDataChanged、NodeChildrenChanged
    • 连接层事件:Disconnected/SyncConnected/Expired/AuthFailed
  3. 一次性语义
    同一个 Watch 对象被触发后自动失效;想持续监听必须重新注册


二、服务端视角:WatchManager + 轻量级指针

  1. 服务端不存“业务回调”,只存“谁对哪条路径哪类事件感兴趣”。

    • DataTree 里每个 ZNode 有三个 WatchManager:
      – dataWatches → getData/exists 挂的
      – childWatches → getChildren 挂的
      – existsWatches → exists 挂的(复用 dataWatches,单独队列方便触发)
  2. 注册过程(以 getData 为例)

    • 读请求走到 FinalRequestProcessor,把(ServerCnxn, path, type) 三元组塞进对应的 WatchManager。
    • ServerCnxn 就是 NIO 的一个连接句柄,内存占用极小(几十字节级)。
  3. 触发过程(setData 为例)

      1. 先改内存 DataTree、写事务日志、版本号 dataVersion++
      1. 把 dataWatches 里该 path 的列表拿出来,整体移到一个临时集合,原队列清空 → 保证一次性
      1. 对每个 ServerCnxn 发一个 WatchEvent 包(只含 path、type、zxid,不含新数据
      1. 网络层把事件塞进客户端输出缓冲区,立即返回,不等待客户端 ACK
  4. 触发与提交顺序

    • 事务必须先成功提交(ZAB 已落盘 + 过半 ACK)才会去触发 Watch;
    • 因此客户端收到事件时,一定可以读到 ≥ 该 zxid 的数据(如果重新连到最新节点)。

三、客户端视角:ZKWatchManager + 异步线程回调

  1. 结构

    • ZKWatchManager 维护三张 HashMap<String, Set>:
      – dataWatches / existWatches / childWatches
    • 每次读操作传 true 时,客户端把用户传入的 Watcher 对象同步放进对应 Map;
    • 网络层收到 WatchEvent 后,IO 线程只负责把事件塞进事件队列,真正回调由 EventThread 单线程顺序派发。
  2. 一次性实现

    • 收到事件后,客户端先把对应 Map 里的 Watcher 删掉,再回调 → 与服务器保持语义一致。
  3. 重连逻辑

    • SyncConnected 时,旧 Watch 会被服务端批量重新注册(见四);
    • Sasl/AuthFailed/Expired 不会恢复,必须用户重新建连接再挂。

四、网络协议:Watch 在 TCP 包里长什么样

  1. 请求挂 Watch

    • 请求头里有一个 watch 布尔标志位(existsRequest、getDataRequest…);
    • 服务端收到 true 才会注册,否则直接返回数据。
  2. 事件推送

    • 响应报文类型为 WATCH_EVENT (-1),格式:
      type(4) + state(4) + path(string)
    • 不带新数据、不带版本号,减轻网络峰值。
  3. 重连批量同步

    • 客户端 Session 重新建立后,会发 SetWatches 请求
      把本地三张 Map 里所有 path 一次性打包发给新服务器;
    • 服务端收到后,瞬间重新注册对应 Watch,保证业务层无感切换。

五、典型坑 & 实战技巧

  1. “看到了事件却读不到新数据”
    原因:客户端重连到旧 Follower,其数据还没同步。
    解决:读之前先 sync(path),强制让该 Server 追一下 zxid。

  2. “事件丢失”
    场景:节点反复快速创建/删除,Watch 还没来得及重新注册。
    解决:

    • 收到 NodeDeleted 后 循环尝试 exists(…, true) 直到成功;
    • 或者使用 递归监听框架(Curator 的 NodeCache/TreeCache)。
  3. “海量 Watch 打爆内存”

    • 一个 ZNode 上挂 10 万个 Watch,触发时要遍历 10 万连接,单线程炸掉
    • 生产建议:
      – 让业务监听固定父节点而不是每个子节点;
      – 用 ephemeral sequential + 子节点变化事件 做分布式锁排队,避免百万 Watch。
  4. 高并发抢主场景

    • 抢主失败后需要反复监听父节点,如果直接用 exists 重置 Watch,会把压力集中到一台 Follower;
    • 优化:所有客户端监听 /leader/ephemeral_ 前缀,收到子节点变化事件后再读取列表,即可分散读压力。

六、一张图总结流程

客户端读请求 → 标记 watch=true
→ 服务端把 (连接,path) 塞进 WatchManager
→ 写请求成功提交 → 服务端抽 Watch 列表 → 发 Event 包
→ 客户端 IO 线程收到 → 删掉本地 Watcher → EventThread 回调用户代码
→ 用户重新注册(若需要)


七、一句话背下来

ZooKeeper 的 Watch 是一次性、轻量级、异步推送的“触发器”:
服务端只记“谁对哪感兴趣”,事件里只告诉你“哪变了”,
不包数据、不保证一直监听
业务层必须主动重新注册 + 版本号/同步 才能做出可靠通知框架


4-

http://www.dtcms.com/a/486130.html

相关文章:

  • 全自动相机在线标定方法
  • 设计模式篇之 中介者模式 Mediator
  • 卫星几何处理
  • 【个人成长笔记】在Ubuntu中的Linux系统安装sogoupinyin搜狗输入法完整版(亲测有效)
  • 3d驱动模型。如何让人物说话?什么情况下需要训练wav2lip模型,自己训练的好处是什么?操作步骤是?
  • 画册什么网站做方便做外汇著名网站
  • CRMEB标准版PHP订单发货全解析
  • Flink 窗口 Join 与区间 Join 实战详解
  • 分布式监控体系:从指标采集到智能告警的完整之道
  • 《Muduo网络库:实现one loop per thread设计模式》
  • 怎么注册网站卖东西哪有培训网站开发
  • makefile概述
  • 用R处理nc文件
  • GaussDB DN动态内存使用满导致DN主备切换
  • 湖南微网站开发北京市建设规划网站
  • TCP与UDP:传输层双雄的核心对比
  • 安化网站建设怎样建个人网站 步骤
  • 并查集-547.省份的数量-力扣(LeetCode)
  • 生命周期全景图:从componentDidMount到getSnapshotBeforeUpdate
  • p2p做网站plc编程入门基础知识
  • 学院个人信息|基于SprinBoot+vue的学院个人信息管理系统(源码+数据库+文档)
  • Unity AB包加载与依赖管理全解析
  • 基于Springboot的游戏网站的设计与实现45nuv3l8(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 深入理解 Vue.js 原理
  • 基于bert-base-chinese的外卖评论情绪分类项目
  • OpenSSL EVP编程介绍
  • 网站服务器组建中国国际贸易网站
  • 上新!功夫系列高通量DPU卡 CONFLUX®-2200P 全新升级,带宽升 40% IOPS提60%,赋能多业务场景。
  • Spring Boot 3零基础教程,properties文件中配置和类的属性绑定,笔记14
  • 以数据智能重构 OTC 连锁增长逻辑,覆盖网络与合作生态双维赛跑