在阅读 ZooKeeper 的lock 配方时,我感到很困惑。似乎这个分布式锁的秘诀不能保证“任何快照及时没有两个客户端认为他们持有相同的锁”。但是既然ZooKeeper被广泛采用,如果参考文档中出现这样的错误,早就应该有人指出来了,那我误会了什么?
引用分布式锁的秘诀:
锁具
全局同步的完全分布式锁,这意味着在任何时间快照中,没有两个客户端认为他们持有相同的锁。这些可以使用 ZooKeeper 来实现。与优先级队列一样,首先定义一个锁节点。
- 使用路径名为“ locknode /guid-lock-”并设置序列和临时标志调用 create() 。
- 在不设置监视标志的情况下在锁定节点上调用 getChildren()(这对于避免羊群效应很重要)。
- 如果在步骤 1 中创建的路径名具有最小的序列号后缀,则客户端具有锁定并且客户端退出协议。
- 客户端调用 exists( ) 并在锁定目录中的路径上设置了监视标志,并具有下一个最低序列号。
- 如果 exists( ) 返回 false,则转到第 2 步。否则,请等待上一步的路径名通知,然后再转到第 2 步。
考虑以下情况:
- Client1 成功获取锁(在步骤 3 中),ZooKeeper 节点为“locknode/guid-lock-0”;
- Client2 创建了节点“locknode/guid-lock-1”,获取锁失败,正在观看“locknode/guid-lock-0”;
- 后来,由于某种原因(比如网络拥塞),Client1 未能按时向 ZooKeeper 集群发送心跳消息,但 Client1 仍在工作,错误地认为它仍然持有锁。
但是,ZooKeeper 可能会认为 Client1 的会话超时,然后
- 删除“locknode/guid-lock-0”,
- 向 Client2 发送通知(或者可能先发送通知?),
- 但无法及时向 Client1 发送“会话超时”通知(例如,由于网络拥塞)。
- Client2 收到通知,转到步骤 2,获取它自己创建的唯一节点 ""locknode/guid-lock-1";因此,Client2 假定它持有锁。
- 但同时,Client1 假设它持有锁。
这是一个有效的场景吗?