Zookeeper 相关笔记 | 青训营笔记

100 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天。

本文以 CC-BY-SA 4.0 发布。

Zookeeper

Zookeeper 直译为动物园管理员。 根据文档, 它可以用来为分布式系统提供配置信息、命名、分布式同步和分组服务。 这些服务其实也已经成为了现今的分布式系统里不可或缺的一部分。

当然,用其它的服务,例如 Redis 或是 etcd 也可以通过额外写相应的代码来实现这些功能, 但是,实现这些功能、对其进行 debug(特别是并发等的临界状态下的 bug)并不是一件容易的事情。 而最终这部分代码维护、管理起来也困难, 不同服务(例如不同语言写成的)之间要保持一致并进行共同部署也更加困难。

比如我之前手写了一个分布式节点 ID 分发的方法 (使用 etcd 分配全局唯一节点 ID | 青训营笔记), 但是 Zookeeper 已经提供了与之类似的持久/临时顺序节点的功能,不必重造轮子。

理解

和 etcd 类似,Zookeeper 其实也可以看作是一个键值数据库, 不同的是 Zookeeper 明确有一个类似文件系统的目录的层次模型。

graph TD
    root["/"] --> etc["/etc"]
    etc --> service1["/etc/service1"]
    etc --> service2["/etc/service2"]
    service1 --> conf1["/etc/service1/conf1"]
    service1 --> conf2["/etc/service1/conf2"]

Zookeeper 的一个树状节点可以分为多个种类:

  • PERSISTENT: 持久性
  • EPHEMERAL: 临时,与 etcd 的附上了 lease 的键值对类似,需要客户端心跳进行 keepalive
  • PERSISTENT_SEQUENTIAL: PERSISTENT + 顺序性
  • EPHEMERAL_SEQUENTIAL: EPHEMERAL + 顺序性

分配全局唯一节点 ID 的方案

思路其实与 etcd 类似,但是利用 Zookeeper 的一些功能我们可以更方便地实现以下功能:

  • 每个节点获得一个独立 ID
  • ID 在节点下线之后可以自动回收

思路是:

  1. 创建一个持久性节点 /ID_GEN,用来不断生成顺序的 ID;
  2. 创建一个持久性节点 /NODE_ID,用来记录已经上线的节点;
  3. 每有一个节点上线,则先在 /ID_GEN 下创建一个临时顺序节点 /ID_GEN/idXXXXXXXXXX, 其中 XXXXXXXXXX 就是 Zookeeper 自动分配的序列号;
  4. 因为十位数可能超过了理想 ID 的范围,所以我们需要取模成 YYYY 之后尝试创建一个临时节点 /NODE_ID/idYYYY, 用来记录我们这一 ID 已被使用;
  5. 如果临时节点创建失败,则说明已经有其它节点使用了相同的 ID,我们需要从 3. 开始重试。

整体流程和 etcd 下一致。不同的是因为 Zookeeper 提供了一个获取顺序数字的方法, 让我们可能更容易地进行整体规划,而 etcd 则是需要从 CAS 的原子操作写起, 需要自行设计获取顺序数字的方法。