Apache Curator之InterProcessMutex源码分析(四)
上篇文章通过秒购的例子对InterProcessMutex锁有了初步认识,本文将通过对源码进行分析带你进入分布式锁的世界。
老规矩先上图,为了更清晰的了解获取锁,释放锁的过程,下图简化了一些细节,使整个流程更加通畅。
下面将逐个方法去分析。
InterProcessMutex.acquire()
1 @Override
//获得分布式锁,阻塞 2 public void acquire() throws Exception { 3 if (!internalLock(-1, null)) { 4 throw new IOException("Lost connection while trying to acquire lock: " + basePath); 5 } 6 } 7
//获得分布式锁,在指定的时间内阻塞,推荐使用 8 @Override 9 public boolean acquire(long time, TimeUnit unit) throws Exception { 10 return internalLock(time, unit); 11 }
InterProcessMutex.internalLock(long time, TimeUnit unit)
1 private boolean internalLock(long time, TimeUnit unit) throws Exception { 2 3 Thread currentThread = Thread.currentThread(); 4 //获得当前线程的锁对象 5 LockData lockData = threadData.get(currentThread); 6 if (lockData != null) { 7 // re-entering
//如果锁不为空,当前线程已经获得锁,可重入锁,lockCount++ 8 lockData.lockCount.incrementAndGet(); 9 return true; 10 } 11
//获取锁,返回锁的节点路径 12 String lockPath = internals.attemptLock(time, unit, getLockNodeBytes()); 13 if (lockPath != null) {
//将当前线程的锁对象信息保存起来 14 LockData newLockData = new LockData(currentThread, lockPath); 15 threadData.put(currentThread, newLockData); 16 return true; 17 } 18 19 return false; 20 }
LockInternals.attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes)
1 String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception { 2 final long startMillis = System.currentTimeMillis(); 3 final Long millisToWait = (unit != null) ? unit.toMillis(time) : null;//等待时间,毫秒 4 final byte[] localLockNodeBytes = (revocable.get() != null) ? new byte[0] : lockNodeBytes; 5 int retryCount = 0; 6 7 String ourPath = null; 8 boolean hasTheLock = false; 9 boolean isDone = false; 10 while (!isDone) { 11 isDone = true; 12 13 try {
//在当前path下创建临时有序节点 14 ourPath = driver.createsTheLock(client, path, localLockNodeBytes);
//判断是不是序号最小的节点,如果是返回true,否则阻塞等待 15 hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath); 16 } catch (KeeperException.NoNodeException e) { 17 if (client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper())) { 18 isDone = false; 19 } else { 20 throw e; 21 } 22 } 23 } 24 25 if (hasTheLock) { 26 return ourPath; 27 } 28 29 return null; 30 }
StandardLockInternalsDriver.createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes)
1 @Override 2 public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception { 3 String ourPath;
//在zookeeper的指定路径上,创建一个临时序列节点。只是纯粹的创建了一个节点,并不是说线程已经持有了锁。 4 if (lockNodeBytes != null) { 5 ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes); 6 } else { 7 ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path); 8 } 9 return ourPath; 10 }
LockInternals.internalLockLoop(long startMillis, Long millisToWait, String ourPath)
1 private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception { 2 boolean haveTheLock = false; 3 boolean doDelete = false; 4 try { 5 if (revocable.get() != null) { 6 client.getData().usingWatcher(revocableWatcher).forPath(ourPath); 7 } 8 9 while ((client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock) {
//获得所有子节点 10 List<String> children = getSortedChildren(); 11 String sequenceNodeName = ourPath.substring(basePath.length() + 1); // +1 to include the slash 12 13 PredicateResults predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases);
//判断是否是最小节点 14 if (predicateResults.getsTheLock()) { 15 haveTheLock = true; 16 } else { 17 String previousSequencePath = basePath + "/" + predicateResults.getPathToWatch(); 18 //同步,是为了实现公平锁 19 synchronized (this) { 20 try {
//给比自己小的节点设置监听器 21 client.getData().usingWatcher(watcher).forPath(previousSequencePath); 22 if (millisToWait != null) { 23 millisToWait -= (System.currentTimeMillis() - startMillis); 24 startMillis = System.currentTimeMillis();
//等待超时,超时删除临时节点,超时时间是acquire方法传入的参数 25 if (millisToWait <= 0) { 26 doDelete = true; // timed out - delete our node 27 break; 28 } 29 //没有超时,继续等待 30 wait(millisToWait); 31 } else {
//如果等待时间==null,一直阻塞等待 32 wait(); 33 } 34 } catch (KeeperException.NoNodeException e) { 35 // it has been deleted (i.e. lock released). Try to acquire again 36 } 37 } 38 } 39 } 40 } catch (Exception e) { 41 ThreadUtils.checkInterrupted(e); 42 doDelete = true; 43 throw e; 44 } finally { 45 if (doDelete) { 46 deleteOurPath(ourPath);//如果如果抛出异常或超时,都会删除临时节点 47 } 48 } 49 return haveTheLock; 50 }
LockInternals.getSortedChildren()
1 List<String> getSortedChildren() throws Exception { 2 return getSortedChildren(client, basePath, lockName, driver); 3 } 4 5 public static List<String> getSortedChildren(CuratorFramework client, String basePath, final String lockName, final LockInternalsSorter sorter) throws Exception { 6 try {
//获得basePath下所有子节点,进行排序 7 List<String> children = client.getChildren().forPath(basePath); 8 List<String> sortedList = Lists.newArrayList(children); 9 Collections.sort(sortedList, new Comparator<String>() { 10 @Override 11 public int compare(String lhs, String rhs) { 12 return sorter.fixForSorting(lhs, lockName).compareTo(sorter.fixForSorting(rhs, lockName)); 13 } 14 }); 15 return sortedList; 16 } catch (KeeperException.NoNodeException ignore) { 17 return Collections.emptyList(); 18 } 19 }
StandardLockInternalsDriver.getsTheLock(CuratorFramework c, List<String> child, String nodeName, int max)
1 @Override 2 public PredicateResults getsTheLock(CuratorFramework client, List<String> children, String sequenceNodeName, int maxLeases) throws Exception { 3 int ourIndex = children.indexOf(sequenceNodeName); 4 validateOurIndex(sequenceNodeName, ourIndex); 5 6 boolean getsTheLock = ourIndex < maxLeases; 7 String pathToWatch = getsTheLock ? null : children.get(ourIndex - maxLeases); 8 9 return new PredicateResults(pathToWatch, getsTheLock); 10 }
LockInternals.deleteOurPath(String ourPath)
1 private void deleteOurPath(String ourPath) throws Exception { 2 try { 3 client.delete().guaranteed().forPath(ourPath); 4 } catch (KeeperException.NoNodeException e) { 5 // ignore - already deleted (possibly expired session, etc.) 6 } 7 }
InterProcessMutex.release()
1 @Override 2 public void release() throws Exception { 3 /* 4 Note on concurrency: a given lockData instance 5 can be only acted on by a single thread so locking isn't necessary 6 */ 7 8 Thread currentThread = Thread.currentThread(); 9 LockData lockData = threadData.get(currentThread);
//如果当前线程没有持有锁,不能释放 10 if (lockData == null) { 11 throw new IllegalMonitorStateException("You do not own the lock: " + basePath); 12 } 13
//重入锁计数减一,如果还大于0,不能释放。直到所有重入业务完成,计数为0才能释放 14 int newLockCount = lockData.lockCount.decrementAndGet(); 15 if (newLockCount > 0) { 16 return; 17 } 18 if (newLockCount < 0) { 19 throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + basePath); 20 } 21 try { 22 internals.releaseLock(lockData.lockPath); 23 } finally { 24 threadData.remove(currentThread); 25 } 26 }
LockInternals.release()
1 final void releaseLock(String lockPath) throws Exception { 2 client.removeWatchers();//删除监听 3 revocable.set(null); 4 deleteOurPath(lockPath);//删除Path 5 }