【Zookeekper】分布锁Curator

  1. 有序节点:假如当前有一个父节点为/lock,咱们能够在这个父节点下面建立子节点;zookeeper提供了一个可选的有序特性,例如咱们能够建立子节点“/lock/node-”而且指明有序,那么zookeeper在生成子节点时会根据当前的子节点数量自动添加整数序号,也就是说若是是第一个建立的子节点,那么生成的子节点为/lock/node-0000000000,下一个节点则为/lock/node-0000000001,依次类推。
  2. 临时节点:客户端能够创建一个临时节点,在会话结束或者会话超时后,zookeeper会自动删除该节点。
  3. 事件监听:在读取数据时,咱们能够同时对节点设置事件监听,当节点数据或结构变化时,zookeeper会通知客户端。当前zookeeper有以下四种事件:1)节点建立;2)节点删除;3)节点数据修改;4)子节点变动。

下面描述使用zookeeper实现分布式锁的算法流程,假设锁空间的根节点为/lock:html

  1. 客户端链接zookeeper,并在/lock下建立临时的有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。
  2. 客户端获取/lock下的子节点列表,判断本身建立的子节点是否为当前子节点列表中序号最小的子节点,若是是则认为得到锁,不然监听/lock的子节点变动消息,得到子节点变动通知后重复此步骤直至得到锁;
  3. 执行业务代码;
  4. 完成业务流程后,删除对应的子节点释放锁。

步骤1中建立的临时节点可以保证在故障的状况下锁也能被释放,考虑这么个场景:假如客户端a当前建立的子节点为序号最小的节点,得到锁以后客户端所在机器宕机了,客户端没有主动删除子节点;若是建立的是永久的节点,那么这个锁永远不会释放,致使死锁;因为建立的是临时节点,客户端宕机后,过了必定时间zookeeper没有收到客户端的心跳包判断会话失效,将临时节点删除从而释放锁。java

另外细心的朋友可能会想到,在步骤2中获取子节点列表与设置监听这两步操做的原子性问题,考虑这么个场景:客户端a对应子节点为/lock/lock-0000000000,客户端b对应子节点为/lock/lock-0000000001,客户端b获取子节点列表时发现本身不是序号最小的,可是在设置监听器前客户端a完成业务流程删除了子节点/lock/lock-0000000000,客户端b设置的监听器岂不是丢失了这个事件从而致使永远等待了?这个问题不存在的。由于zookeeper提供的API中设置监听器的操做与读操做是原子执行的,也就是说在读子节点列表时同时设置监听器,保证不会丢失事件。node

最后,对于这个算法有个极大的优化点:假如当前有1000个节点在等待锁,若是得到锁的客户端释放锁时,这1000个客户端都会被唤醒,这种状况称为“羊群效应”;在这种羊群效应中,zookeeper须要通知1000个客户端,这会阻塞其余的操做,最好的状况应该只唤醒新的最小节点对应的客户端。应该怎么作呢?在设置事件监听时,每一个客户端应该对恰好在它以前的子节点设置事件监听,例如子节点列表为/lock/lock-0000000000、/lock/lock-000000000一、/lock/lock-0000000002,序号为1的客户端监听序号为0的子节点删除消息,序号为2的监听序号为1的子节点删除消息。算法

因此调整后的分布式锁算法流程以下:apache

  1. 客户端链接zookeeper,并在/lock下建立临时的有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。
  2. 客户端获取/lock下的子节点列表,判断本身建立的子节点是否为当前子节点列表中序号最小的子节点,若是是则认为得到锁,不然监听恰好在本身以前一位的子节点删除消息,得到子节点变动通知后重复此步骤直至得到锁;
  3. 执行业务代码;
  4. 完成业务流程后,删除对应的子节点释放锁。

Curator实现

<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.0.0</version>
</dependency>
public static void main(String[] args) throws Exception {
    //建立zookeeper的客户端
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
    CuratorFramework client = CuratorFrameworkFactory.newClient("10.21.41.181:2181,10.21.42.47:2181,10.21.49.252:2181", retryPolicy);
    client.start();

    //建立分布式锁, 锁空间的根节点路径为/curator/lock
    InterProcessMutex mutex = new InterProcessMutex(client, "/curator/lock");
    mutex.acquire();
    //得到了锁, 进行业务流程
    System.out.println("Enter mutex");
    //完成业务流程, 释放锁
    mutex.release();
    
    //关闭客户端
    client.close();
}

  

摘录自:http://www.dengshenyu.com/java/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/10/23/zookeeper-distributed-lock.html分布式

相关文章
相关标签/搜索