Zookeeper 分布式锁

来源:互联网 发布:淘宝镜子自拍模特设备 编辑:程序博客网 时间:2024/06/03 20:44

转载自:http://blog.csdn.net/java2000_wl/article/details/8694270

获取锁实现思路:

1.     首先创建一个作为锁目录(znode),通常用它来描述锁定的实体,称为:/lock_node

2.     希望获得锁的客户端在锁目录下创建znode,作为锁/lock_node的子节点,并且节点类型为有序临时节点(EPHEMERAL_SEQUENTIAL);

        例如:有两个客户端创建znode,分别为/lock_node/lock-1和/lock_node/lock-2

3.     当前客户端调用getChildren(/lock_node)得到锁目录所有子节点,不设置watch,接着获取小于自己(步骤2创建)的兄弟节点

4.     步骤3中获取小于自己的节点不存在 && 最小节点与步骤2中创建的相同,说明当前客户端顺序号最小,获得锁,结束。

5.     客户端监视(watch)相对自己次小的有序临时节点状态

6.     如果监视的次小节点状态发生变化,则跳转到步骤3,继续后续操作,直到退出锁竞争。

[java] view plain copy
 print?
  1. public synchronized boolean lock() throws KeeperException, InterruptedException {  
  2.        if (isClosed()) {  
  3.            return false;  
  4.        }  
  5.        // 如果锁目录不存在, 创建锁目录   节点类型为永久类型  
  6.        ensurePathExists(dir);  
  7.   
  8.        // 创建锁节点,节点类型EPHEMERAL_SEQUENTIAL   
  9.        // 如果不存在小于自己的节点   并且最小节点 与当前创建的节点相同  获得锁  
  10.        // 未获得成功,对当前次小节点设置watch  
  11.        return (Boolean) retryOperation(zop);  
  12.    }  

创建锁目录

[java] view plain copy
 print?
  1. protected void ensurePathExists(String path) {  
  2.     ensureExists(path, null, acl, CreateMode.PERSISTENT);  
  3. }  
[java] view plain copy
 print?
  1. protected void ensureExists(final String path, final byte[] data,  
  2.         final List<ACL> acl, final CreateMode flags) {  
  3.     try {  
  4.         retryOperation(new ZooKeeperOperation() {  
  5.             public boolean execute() throws KeeperException, InterruptedException {  
  6.                 // 创建锁目录  
  7.                 Stat stat = zookeeper.exists(path, false);  
  8.                 // 节点如果存在  直接返回  
  9.                 if (stat != null) {  
  10.                     return true;  
  11.                 }  
  12.                 // 创建节点  
  13.                 // data为null  
  14.                 // flags为持久化节点  
  15.                 zookeeper.create(path, data, acl, flags);  
  16.                 return true;  
  17.             }  
  18.         });  
  19.     } catch (KeeperException e) {  
  20.         LOG.warn("Caught: " + e, e);  
  21.     } catch (InterruptedException e) {  
  22.         LOG.warn("Caught: " + e, e);  
  23.     }  
  24. }  

创建锁节点,获得锁目录下的所有节点, 如果为最小节点 获得锁成功

[java] view plain copy
 print?
  1.     /** 
  2.      * the command that is run and retried for actually  
  3.      * obtaining the lock 
  4.      * @return if the command was successful or not 
  5.      */  
  6.     public boolean execute() throws KeeperException, InterruptedException {  
  7.         do {  
  8.             if (id == null) {  
  9.                 long sessionId = zookeeper.getSessionId();  
  10.                 String prefix = "x-" + sessionId + "-";  
  11.                 // lets try look up the current ID if we failed   
  12.                 // in the middle of creating the znode  
  13.                 findPrefixInChildren(prefix, zookeeper, dir);  
  14.                 idName = new ZNodeName(id);  
  15.             }  
  16.             if (id != null) {  
  17.                 List<String> names = zookeeper.getChildren(dir, false);  
  18.                 if (names.isEmpty()) {  
  19.                     LOG.warn("No children in: " + dir + " when we've just " +  
  20.                     "created one! Lets recreate it...");  
  21.                     // lets force the recreation of the id  
  22.                     id = null;  
  23.                 } else {  
  24.                     // lets sort them explicitly (though they do seem to come back in order ususally :)  
  25.                     SortedSet<ZNodeName> sortedNames = new TreeSet<ZNodeName>();  
  26.                     for (String name : names) {  
  27.                         sortedNames.add(new ZNodeName(dir + "/" + name));  
  28.                     }  
  29.                     // 获得最小节点  
  30.                     ownerId = sortedNames.first().getName();  
  31.                     // lock_1, lock_2, lock_3  传入参数lock_2  返回lock_1  
  32.                     SortedSet<ZNodeName> lessThanMe = sortedNames.headSet(idName);  
  33.                     if (!lessThanMe.isEmpty()) {  
  34.                         ZNodeName lastChildName = lessThanMe.last();  
  35.                         lastChildId = lastChildName.getName();  
  36.                         if (LOG.isDebugEnabled()) {  
  37.                             LOG.debug("watching less than me node: " + lastChildId);  
  38.                         }  
  39.                         // 次小节点设置watch   
  40.                         Stat stat = zookeeper.exists(lastChildId, new LockWatcher());  
  41.                         if (stat != null) {  
  42.                             return Boolean.FALSE;  
  43.                         } else {  
  44.                             LOG.warn("Could not find the" +  
  45.                                     " stats for less than me: " + lastChildName.getName());  
  46.                         }  
  47.                     } else {  
  48.                         // 锁目录下的最小节点  与当前客户端创建相同  
  49.                         if (isOwner()) {  
  50.                             if (callback != null) {  
  51.                                 callback.lockAcquired();  
  52.                             }  
  53.                             // 获得锁  
  54.                             return Boolean.TRUE;  
  55.                         }  
  56.                     }  
  57.                 }  
  58.             }  
  59.         }  
  60.         while (id == null);  
  61.         return Boolean.FALSE;  
  62.     }  
  63. };  

[java] view plain copy
 print?
  1. private void findPrefixInChildren(String prefix, ZooKeeper zookeeper, String dir)   
  2.             throws KeeperException, InterruptedException {  
  3.             // 获取锁目录下的所有子节点  
  4.             List<String> names = zookeeper.getChildren(dir, false);  
  5.             for (String name : names) {  
  6.                 //x-sessionId-  
  7.                 if (name.startsWith(prefix)) {  
  8.                     id = name;  
  9.                     if (LOG.isDebugEnabled()) {  
  10.                         LOG.debug("Found id created last time: " + id);  
  11.                     }  
  12.                     break;  
  13.                 }  
  14.             }  
  15.             // 当前锁目录下   没有与当前会话对应的子节点    创建子节点  节点类型为临时顺序节点  
  16.             if (id == null) {  
  17.                 // dir/x-sessionId-i  
  18.                 id = zookeeper.create(dir + "/" + prefix, data,   
  19.                         getAcl(), EPHEMERAL_SEQUENTIAL);  
  20.   
  21.                 if (LOG.isDebugEnabled()) {  
  22.                     LOG.debug("Created id: " + id);  
  23.                 }  
  24.             }  

释放锁:

释放锁非常简单,删除步骤1中创建的有序临时节点。另外,如果客户端进程死亡或连接失效,对应的节点也会被删除。

[java] view plain copy
 print?
  1. public synchronized void unlock() throws RuntimeException {  
  2.          
  3.        if (!isClosed() && id != null) {  
  4.            // we don't need to retry this operation in the case of failure  
  5.            // as ZK will remove ephemeral files and we don't wanna hang  
  6.            // this process when closing if we cannot reconnect to ZK  
  7.            try {  
  8.                  
  9.                ZooKeeperOperation zopdel = new ZooKeeperOperation() {  
  10.                    public boolean execute() throws KeeperException,  
  11.                        InterruptedException {  
  12.                     // 删除节点  忽略版本  
  13.                        zookeeper.delete(id, -1);     
  14.                        return Boolean.TRUE;  
  15.                    }  
  16.                };  
  17.                zopdel.execute();  
  18.            } catch (InterruptedException e) {  
  19.                LOG.warn("Caught: " + e, e);  
  20.                //set that we have been interrupted.  
  21.               Thread.currentThread().interrupt();  
  22.            } catch (KeeperException.NoNodeException e) {  
  23.                // do nothing  
  24.            } catch (KeeperException e) {  
  25.                LOG.warn("Caught: " + e, e);  
  26.                throw (RuntimeException) new RuntimeException(e.getMessage()).  
  27.                    initCause(e);  
  28.            }  
  29.            finally {  
  30.                if (callback != null) {  
  31.                    callback.lockReleased();  
  32.                }  
  33.                id = null;  
  34.            }  
  35.        }  
  36.    }  
0 0
原创粉丝点击