关于动物园管理员锁定食谱的担忧

hul*_*ier 19 apache-zookeeper

在阅读ZooKeeper的锁定配方时,我感到困惑.似乎这个分布式锁定的配方无法保证"任何两个客户端认为它们持有相同锁定的任何快照".但是由于ZooKeeper被如此广泛采用,如果参考文档中存在这样的错误,有人应该在很久以前就指出它,那么我误解了什么呢?

引用分布式锁的配方:

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

  1. 使用路径名" locknode/guid-lock-" 调用create(),并设置序列和短暂标志.
  2. 在锁定节点上调用getChildren()而不设置监视标志(这对于避免群体效应很重要).
  3. 如果在步骤1中创建的路径名具有最低序列号后缀,则客户端具有锁,并且客户端退出协议.
  4. 客户端调用exists(),并在lock目录中的路径上设置watch标志,并使用下一个最低序列号.
  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认为它持有锁.

这是一个有效的场景吗?

sbr*_*ges 15

您描述的场景可能会出现.客户端1认为它具有锁定,但实际上其会话已超时,并且客户端2获取锁定.

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

  • 违反"在任何时间快照"不变的另一种简单方法(因此证明该语句为false)是持有锁的客户端上的长GC暂停.例如:客户端C获取锁定,同时保持java GC启动并冻结该过程60秒.在这些秒之后,C会话到期,另一个进程获取锁.时间11是"2个客户认为他们拥有相同锁定时的快照". (3认同)
  • 刚刚发现这篇有趣的帖子:https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html (2认同)