【问题标题】:Concerns about zookeeper's lock-recipe对 zookeeper 的 lock-recipe 的担忧
【发布时间】:2012-12-25 21:13:54
【问题描述】:

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

引用the recipe for distributed locks:

全局同步的完全分布式锁,意味着在任何时间快照中,没有两个客户端认为他们持有相同的锁。这些可以使用 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 假定它持有锁。

这是一个有效的场景吗?

【问题讨论】:

标签: apache-zookeeper


【解决方案1】:

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

来自 Zookeeper 文档:

  • 删除节点只会导致一个客户端唤醒,因为 每个节点都由一个客户端监视。这样,你避免 羊群效应。
  • 没有轮询或超时。

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

【讨论】:

  • 谢谢;但我不太明白你的想法;对我来说,“避免羊群效应”&&“没有轮询或超时”不能确保 client2 在 client1 持有锁时无法获取锁。另外,临时节点会在会话超时后被ZK自动删除,所以我也看不到“挂锁的风险”......
  • 如果 Client1 的会话超时,那么在这种情况下锁不再存在,但是 Client1 会在那个时候从 Zookeeper 获取 SESSION_EXPIRED 或 CONNECTION_LOSS,所以他们会知道他们已经失去了连接。
  • 但是如果 SESSION_EXPIRED 消息没有及时发送到 Client1 怎么办?由于暂时的高丢包率,例如(TCP连接保持ESTABLISHED)
  • 您得到的是 connection_loss,而不是 session_expired。您必须连接到 zookeeper 才能使会话过期。
【解决方案2】:

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

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

【讨论】:

  • 谢谢;这是否意味着,“不变”——在任何时间快照中都没有两个客户端认为他们持有相同的锁不成立?
  • 另一种违反“在任何时间快照”不变量(从而证明该语句为假)的简单方法是在持有锁的客户端上长时间 GC 暂停。例如:客户端 C 获取锁,同时持有它 java GC 启动并冻结进程 60 秒。在其中的 10 秒后,C 会话到期,另一个进程获取锁。时间 11 是“当 2 个客户端认为他们持有相同的锁时的快照”。
  • 刚刚发现这个有趣的帖子:martin.kleppmann.com/2016/02/08/…
  • 在任何时间快照中,没有两个客户端认为他们持有相同的锁是基于以下假设:有限的网络延迟、有限的进程暂停和有限的时钟错误。跨度>
  • 此策展人技术说明适用于 GC 暂停案例:cwiki.apache.org/confluence/display/CURATOR/TN10
【解决方案3】:

来自packt book - Zookeeper Essentials

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

【讨论】:

    猜你喜欢
    • 2014-11-01
    • 1970-01-01
    • 2020-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-30
    • 2010-11-27
    相关资源
    最近更新 更多