《ZooKeeper 节点类型与分布式锁原理》

《ZooKeeper 节点类型与分布式锁原理》

ZooKeeper 节点是有生命周期的,具体取决于节点的类型。ZooKeeper 中的节点类型可分为持久节点(PERSISTENT)、临时节点(EPHEMERAL)以及时序节点(SEQUENTIAL),组合使用可以生成以下 4 种节点类型。

1 四种节点类型

  • 持久节点(PERSISTENT):节点创建后一直存在,直到有删除操作主动清除——不会因为创建该节点的客户端会话失效而消失。

  • 持久顺序节点(PERSISTENT_SEQUENTIAL):基本特性与持久节点一致。额外特性是,每个父节点会为它的第一级子节点维护一份时序,记录每个子节点创建的先后顺序。创建子节点时可设置该属性,ZooKeeper 会自动为给定节点名加上一个数字后缀作为新的节点名,该数字后缀基于整型最大值向上增长。

  • 临时节点(EPHEMERAL):生命周期与客户端会话绑定,客户端会话失效节点会自动被清除。注意:是“会话失效”而非“连接断开”。另外,临时节点下面不能创建子节点。

  • 临时顺序节点(EPHEMERAL_SEQUENTIAL):结合了临时节点与会话失效自动清除、顺序节点带自增后缀的特性,常用于实现分布式锁。

2 基于临时顺序节点实现的分布式锁

经典的非阻塞分布式锁流程如下:

  1. 客户端调用 create() 方法创建名为 _locknode_/guid-lock- 的节点,节点创建类型设置为 EPHEMERAL_SEQUENTIAL。
  2. 客户端调用 getChildren("_locknode_") 获取所有已创建的子节点(此处不注册任何 Watcher)。
  3. 客户端获取到所有子节点 path 后,如果发现自己创建的节点序号最小,就认为该客户端获得了锁。

如果发现自己并非所有子节点中序号最小的,说明还没获得锁。此时客户端需要找到比自己序号小的那个节点,对其调用 exists() 方法并注册事件监听:

  • 当被关注的节点被移除时,客户端会收到相应通知;
  • 收到通知后再次调用 getChildren("_locknode_") 获取所有已创建的子节点,确认自己确实是最小的节点,然后重新进入获取锁的判断流程。

该方案相比“单临时节点锁”的优点在于避免了惊群效应:每个客户端只监听前一个节点,节点释放时只有下一个客户端被唤醒。

阅读 — · 全站 —
🎸 我的歌单 0 首