$ zookeeper实现分布式锁

$

完全分布式的锁是全局同步的,意味着任何时刻不会有两个客户端认为他们持有同一把锁,这可以用zookeeper来实现,首先定义一个锁节点。

client加锁的操作如下:

  1. 调用 create( ) 方法创建一个路径名为_locknode_/lock-的临时有序节点路径。
  2. 在锁节点上调用**getChildren( )**方法(不要watch该节点以避免惊群效应)。
  3. 如果第1步中创建的路径后缀序号是最小的,则client获得了锁并退出后续的协议。
  4. client在锁目录下比自己序号小一个的路径上调用**exists( )**方法并设置watch标志。
  5. 如果**exists( )**返回false,回到第2步;否则等待第4步中设置的通知然后返回第2步。

client释放锁的协议很简单:只要把第1步中创建的节点删除即可。

需要注意的几点:

  • 移除一个节点只会唤醒一个client,因为每个节点只被一个clientwatch,这样就避免了惊群效应。

  • 不会有轮询或者超时的操作。

  • 由这种实现锁的方式很容易实现查看锁竞争的数量,移除锁,debug锁情况等。

$ 共享锁

进行一些调整就可以实现共享锁:

获取读锁:

  1. 调用 create( ) 方法创建一个路径名为_locknode_/read-的临时有序节点路径。
  2. 在锁节点上调用**getChildren( )**方法(不要watch该节点以避免惊群效应)。
  3. 如果没有子节点的路径名以write-开头并且序号是第1步中创建节点的下一个,则client获得了锁并退出后续的协议。
  4. 对锁目录中路径名以write-开头并且序号是下一个的节点调用**exists( )**方法并设置watch标志。
  5. 如果**exists( )**返回false,回到第2步;否则等待第4步中设置的通知然后返回第2步。

获取写锁:

  1. 调用 create( ) 方法创建一个路径名为_locknode_/write-的临时有序节点路径。
  2. 在锁节点上调用**getChildren( )**方法(不要watch该节点以避免惊群效应)。
  3. 如果第1步中创建的路径后缀序号是最小的,则client获得了锁并退出后续的协议。
  4. client在锁目录下比自己序号小一个的路径上调用**exists( )**方法并设置watch标志。
  5. 如果**exists( )**返回false,回到第2步;否则等待第4步中设置的通知然后返回第2步。

这似乎显得这个方案创建了一个惊群效应:当序号最小的写节点被删除的时候,所有等待获取读锁的client都会感知到。事实上这是有效的行为:所有等待读的client都应该被释放因为他们获取到锁了。惊群效应指的是只有一个或少部分的机器会处理却触发整个集群。

参考:

http://zookeeper.apache.org/doc/r3.4.9/recipes.html

http://www.importnew.com/27019.html

https://colobu.com/2014/12/12/zookeeper-recipes-by-example-2

更新时间: 9/24/2019, 6:01:53 AM