21

在阅读 ZooKeeper 的lock 配方时,我感到很困惑。似乎这个分布式锁的秘诀不能保证“任何快照及时没有两个客户端认为他们持有相同的锁”。但是既然ZooKeeper被广泛采用,如果参考文档中出现这样的错误,早就应该有人指出来了,那我误会了什么?

引用分布式锁的秘诀

锁具

全局同步的完全分布式锁,这意味着在任何时间快照中,没有两个客户端认为他们持有相同的锁。这些可以使用 ZooKeeper 来实现。与优先级队列一样,首先定义一个锁节点。

  1. 使用路径名为“ locknode /guid-lock-”并设置序列和临时标志调用 create() 。
  2. 在不设置监视标志的情况下在锁定节点上调用 getChildren()(这对于避免羊群效应很重要)。
  3. 如果在步骤 1 中创建的路径名具有最小的序列号后缀,则客户端具有锁定并且客户端退出协议。
  4. 客户端调用 exists( ) 并在锁定目录中的路径上设置了监视标志,并具有下一个最低序列号。
  5. 如果 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 的会话超时,然后

    1. 删除“locknode/guid-lock-0”,
    2. 向 Client2 发送通知(或者可能先发送通知?),
    3. 但无法及时向 Client1 发送“会话超时”通知(例如,由于网络拥塞)。
  • Client2 收到通知,转到步骤 2,获取它自己创建的唯一节点 ""locknode/guid-lock-1";因此,Client2 假定它持有锁。
  • 但同时,Client1 假设它持有锁。

这是一个有效的场景吗?

4

3 回答 3

15

您描述的情况可能会出现。客户端 1 认为自己有锁,但实际上它的会话已经超时,客户端 2 获取了锁。

ZooKeeper 客户端库会通知 Client 1 其连接已断开(但客户端直到客户端连接到服务器后才知道会话已过期),因此客户端可以编写一些代码并假设他的锁已丢失如果他断开连接的时间过长。但是使用锁的线程需要定期检查锁是否仍然有效,这本质上是活泼的。

于 2013-01-11T14:32:20.240 回答
0

...但是,Zookeeper 可能认为 client1 的会话超时,然后...

来自 Zookeeper 文档:

  • 删除一个节点只会导致一个客户端唤醒,因为每个节点都被一个客户端监视。通过这种方式,您可以避免羊群效应。
  • 没有轮询或超时。

所以我认为你描述的问题不会出现。在我看来,如果创建它们的客户端发生某些事情,可能会有挂锁的风险,但你描述的场景不应该出现。

于 2013-01-11T12:04:10.840 回答
0

来自packt book - Zookeeper Essentials

如果由于连接丢失导致创建znode部分失败,则客户端可能无法正确确定是否成功创建了子znode。为了解决这种情况,客户端可以将其会话 ID 存储在 znode 数据字段中,甚至作为 znode 名称本身的一部分。由于客户端在重新连接后保留相同的会话 ID,因此可以通过查看会话 ID 轻松确定子 znode 是否由它创建。

于 2021-12-17T12:37:43.293 回答