用 ZooKeeper 做注册中心,先要定节点怎么摆:按服务分层还是平铺?实例信息放节点名还是节点数据?这篇用 1000 个服务、5000 个实例做对比,量读取字节、无关变更引起的回调数、URL 作节点名的陷阱和 watch 数量。
建节点:分层 /h 下每个服务 5 个实例(共 1000 个服务目录);平铺 /f 下 5000 个节点 消费者想知道 svc0042 的实例列表:分层 get_children 返回 69 字节;平铺要拉整个目录 85024 字节 随机改 100 次不同服务的实例列表(每次一建一删):订阅 svc0042 的分层消费者回调 0 次;订阅整个平铺目录的消费者回调 104 次(每次回调都要重新拉 85024 字节)
分层让目录等于「订阅边界」:消费者只关心自己依赖的服务,所以只 watch 那几个目录。平铺的目录越大,每次变更触发的 get_children 响应越大(响应里是全部子节点名,见大目录)。
直接 create /reg/prov/dubbo://10.0.0.1:20880/com.foo.Bar?weight=100 → NoNodeError(/ 被当成层级分隔符,试图在不存在的 dubbo:/10.0.0.1:20880/... 下建节点) URL 编码后 create 成功:/reg/prov/dubbo%3A%2F%2F10.0.0.1%3A20880%2Fcom.foo.Bar%3Fweight%3D100 读回列表并解码: ['dubbo://10.0.0.1:20880/com.foo.Bar?weight=100']
Dubbo 的注册中心就是这样:提供者的完整 URL 经 URL 编码后作为 /dubbo/<服务>/providers/ 下的节点名。具体源码见第 101 篇。
(开始前服务端上的 watch 总数 = 0) 元信息编码进节点名(只有目录上的 1 个 children watch):服务端为该消费者维护 1 个 watch;元信息放节点数据(每个实例一个 DataWatch + 目录一个 ChildrenWatch):201 个 watch 改一个实例的权重:数据方式下消费者收到 1 次回调(只针对那个实例);名字方式要删旧节点、建新节点,消费者的 children 回调 2 次,每次回调后要重新读整个 200 项的目录
常用折中:不变的信息(地址、协议)放节点名,会变的(权重、状态)放数据并只对确实要变的实例订阅;或者把动态信息放到另一个目录里。