别只会用 getData!Watcher 注册源码流程全拆解

0 阅读4分钟



大家好,我是31岁还在一线写代码、依然热爱技术的小米。

前两天有个粉丝私信我,说他社招面试被问到一个问题:“Zookeeper 客户端注册 Watcher 是怎么实现的?”

他当场愣住了。平时我们写代码都是:

或者:

但真被问到“内部是怎么实现的”,就卡壳了。

今天,我就用一个故事,把这个流程讲透。你会看到:

  • 客户端怎么把 Watcher 带出去?
  • 请求是怎么打包的?
  • 服务端怎么响应?
  • 客户端又是怎么把 Watcher 管起来的?
  • 整个注册过程是怎么闭环的?

在本篇文章,我将带你从源码思维,一步一步拆解。

Watcher 是什么?——它像“保安订阅系统”

我喜欢用一个比喻。把 ZooKeeper 想成一个大型写字楼。

  • /app/config 是某个房间
  • 你是租户
  • 你想知道房间有没有被改造
  • 你雇了一个“保安”(Watcher)

你告诉物业:“这个房间如果装修了,第一时间通知我!”

这个“订阅动作”,就是注册 Watcher。

注册 Watcher 的入口 API

我们常用的三个 API:

源码调用示例:

这里发生了什么?

第一步:调用 API,传入 Watcher

以 getData() 为例。

核心逻辑:

  1. 校验 path
  2. 构造请求对象
  3. 如果 watcher != null
  4. 封装 WatchRegistration

源码核心思想(简化版):

这一步的本质:把 Watcher 和 path 绑定

第二步:封装 WatchRegistration

这个类非常关键。

它干嘛的?负责在客户端收到服务端响应时,把 Watcher 注册到本地管理器。

简化版结构:

实现类:

核心点:

  • 不是马上注册!
  • 而是等服务器响应成功后才注册!

为什么?因为如果服务器返回错误,Watcher不应该生效。

第三步:封装为 Packet 发送给服务端

Zookeeper 客户端内部有个核心类:ClientCnxn。

所有请求都会被封装成:Packet,结构大概这样:

关键点来了:WatchRegistration 被塞进 Packet!

构造代码逻辑(简化):

然后进入发送队列:

然后由发送线程发给服务端。

流程图总结一下

第四步:收到服务端响应

当服务器返回结果后,客户端读取响应:

然后执行:

注意这个 rc 是:

如果是 OK,就执行 register()

第五步:注册到 ZKWatcherManager

Zookeeper 客户端有一个核心管理类:ZKWatcherManager

内部结构:

注册逻辑:

至此,Watcher 才真正“挂”在客户端上。

完整生命周期流程图

我们用故事串起来:

  1. 你打电话给物业(getData)
  2. 告诉他我要订阅这个房间
  3. 物业帮你写申请单(WatchRegistration)
  4. 打包成快递(Packet)
  5. 寄给总部(服务端)
  6. 总部确认
  7. 物业把你加入订阅名单(ZKWatcherManager)

这才完成注册。

一个重要细节:为什么是一次性的?

ZooKeeper 的 Watcher 是一次性的,什么意思?

当事件触发后:

触发完成,Watcher 会被移除。

原因:为了避免状态爆炸和性能问题。

如果想持续监听?你必须:

重新注册。

社招面试该怎么答?

如果面试官问:“Zookeeper 客户端注册 Watcher 流程?”

标准结构回答:

  1. 调用 getData/getChildren/exists 传入 Watcher
  2. 构造 WatchRegistration
  3. 封装进 Packet
  4. 发送到服务端
  5. 收到响应后根据 rc 调用 register
  6. 注册到 ZKWatcherManager
  7. 完成注册

如果你再补一句:“Watcher 注册是延迟到服务端成功响应后才完成的”

面试官会眼睛一亮。

再看一段完整示例代码

注意:事件触发后重新注册。

总结一句话

Zookeeper 客户端注册 Watcher 的核心:延迟注册 + Packet 封装 + 本地管理器统一维护

如果把它抽象成设计模式,其实就是:

  • 观察者模式
  • 延迟执行
  • 回调注册

最后送你一张面试总结表

END

很多人只会用:

但真正懂原理的人,会知道:Watcher 并不是立刻注册,而是在服务端成功响应后才加入本地管理器。

这就是源码思维。社招拼的不是“会用”,而是“理解底层”。

如果你喜欢这种用故事讲源码的方式,点个在看,我们下篇见。