最近准备在项目中引入分布式锁,故而研究基于zookeeper的curator框架。

网上资料不多,自己研究其源码发现,这个框架已经帮我做了很多现成的实现。

下面介绍下锁的实现:

通过源码中LockingExample例子作为切入(推荐多利用现有资源,最快切入),为减小篇幅,代码仅保留关键部分。

curator已经为我们准备好了锁的实现 ----InterProcessMutex,基于zookeeper跨jvm的公平互斥锁实现.

-----------------------------------------------------------------------------------------------------------------------------

1.看下锁的定义,将线程对象和锁对象(线程、路径、锁的数量)关联

    private final ConcurrentMap<Thread, LockData> threadData = Maps.newConcurrentMap();

    private static class LockData
{
final Thread owningThread;
final String lockPath;
final AtomicInteger lockCount = new AtomicInteger(1); private LockData(Thread owningThread, String lockPath)
{
this.owningThread = owningThread;
this.lockPath = lockPath;
}
}

2.获得锁:

  • 如果线程已经有锁,则增加锁的数量,返回  String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());
  • 否则尝试获取锁,获得则加入线程持有锁的MAP,否则返回未获得锁。
    private final LockInternals internals;    

    @Override
public boolean acquire(long time, TimeUnit unit) throws Exception
{
return internalLock(time, unit);
}
private boolean internalLock(long time, TimeUnit unit) throws Exception
{
Thread currentThread = Thread.currentThread(); LockData lockData = threadData.get(currentThread);
if ( lockData != null )
{
lockData.lockCount.incrementAndGet();
return true;
} String lockPath = internals.attemptLock(time, unit, getLockNodeBytes());
if ( lockPath != null )
{
LockData newLockData = new LockData(currentThread, lockPath);
threadData.put(currentThread, newLockData);
return true;
}
return false;
}

3.可以看到,逻辑是基于LockInternals,来看一下他是怎么做到的

  • 通过dirver在锁目录下创建EPHEMERAL_SEQUENTIAL节点
  • 循环尝试获取,基于监听,wait,notifyAll
  1. 获取锁目录下子节点的有序集合
  2. 通过dirver尝试得到PredicateResults(含有是否得到锁及需要监视的目录)
  3. 更新监听信息,开始下一轮尝试
    String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception
{while ( !isDone )
{
isDone = true;
try
{
ourPath = driver.createsTheLock(client, path, localLockNodeBytes);
hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath);
}
}
}
private boolean internalLockLoop(long startMillis, Long millisToWait, String ourPath) throws Exception
{
boolean haveTheLock = false;
boolean doDelete = false;
try
{
if ( revocable.get() != null )
{
client.getData().usingWatcher(revocableWatcher).forPath(ourPath);
} while ( (client.getState() == CuratorFrameworkState.STARTED) && !haveTheLock )
{
List<String> children = getSortedChildren();
String sequenceNodeName = ourPath.substring(basePath.length() + 1); // +1 to include the slash PredicateResults predicateResults = driver.getsTheLock(client, children, sequenceNodeName, maxLeases);
if ( predicateResults.getsTheLock() )
{
haveTheLock = true;
}
else
{
String previousSequencePath = basePath + "/" + predicateResults.getPathToWatch();
System.out.println("ourpath : " + ourPath + " : " + previousSequencePath); synchronized(this)
{
try
{
// use getData() instead of exists() to avoid leaving unneeded watchers which is a type of resource leak
client.getData().usingWatcher(watcher).forPath(previousSequencePath);
if ( millisToWait != null )
{
millisToWait -= (System.currentTimeMillis() - startMillis);
startMillis = System.currentTimeMillis();
if ( millisToWait <= 0 )
{
doDelete = true; // timed out - delete our node
break;
}
wait(millisToWait);
}
else
{
wait();
}
}
catch ( KeeperException.NoNodeException e )
{
}
}
}
}
}
catch ( Exception e )
{
doDelete = true;
throw e;
}
finally
{
if ( doDelete )
{
deleteOurPath(ourPath);
}
}
return haveTheLock;

4.分析下前文中的driver实现,即StandardLockInternalsDriver 关键代码

  • 创建锁是基于目录下创建的EPHEMERAL_SEQUENTIAL节点,即与客户端生命周期相同,并且名字后自动加创建的序列号
  • 得到PredicateResult,如果当前节点为最小节点,则得到锁,getsTheLock为true,否则得到该序列的前一个节点,设为pathToWatch,监控之
    @Override
public PredicateResults getsTheLock(CuratorFramework client, List<String> children, String sequenceNodeName, int maxLeases) throws Exception
{
int ourIndex = children.indexOf(sequenceNodeName);
validateOurIndex(sequenceNodeName, ourIndex);
boolean getsTheLock = ourIndex < maxLeases;
String pathToWatch = getsTheLock ? null : children.get(ourIndex - maxLeases);
return new PredicateResults(pathToWatch, getsTheLock);
} @Override
public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception
{
String ourPath;
if ( lockNodeBytes != null )
{
ourPath = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes);
}
else
{
ourPath = client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path);
}
return ourPath;
}

先发布,后续有心得再续。

最新文章

  1. 《转载》Spring MVC之@RequestBody, @ResponseBody 详解
  2. cut命令
  3. 转:安装MySQL遇到MySQL Server Instance Configuration Wizard未响应的解决办法
  4. Django1.8教程——安装Django
  5. python学习笔记(Tkinter编程利用Treeview实现表格自动更新)
  6. PullToRefresh的使用
  7. hdu 2966 In case of failure k-d树
  8. vim高级编辑(一)
  9. Reporting Service部署之访问权限
  10. delegate vs event
  11. Linux 容器 vs 虚拟机 —— 谁更胜一筹
  12. eclipse连接Android8.0以上手机无法打印日志的问题
  13. 微信小程序电商实战(-)商城首页
  14. Spark性能优化【OOM】
  15. vue 热加载问题
  16. 微信小程序echart 折线图legend不显示的问题
  17. python基础入门学习2
  18. stm32 启动文件 C和汇编交叉嵌入
  19. MySQL 索引原理概述及慢查询优化实战
  20. 报错:无法截断表 '某表',因为该表正由 FOREIGN KEY 约束引用

热门文章

  1. oracle em管理平台
  2. [UE4]Actor的Destroyed事件
  3. centos7 安装pip+python3.6
  4. @postconstruct初始化的操作(转载)
  5. 贪吃蛇 v1.01
  6. 面向对象javascript编程
  7. 利用WordPress REST API 开发微信小程序从入门到放弃
  8. tomcat简单使用(一)
  9. SurFS:共享式和分布式集群各取所长
  10. RxJava响应式编程,入门的HelloWorld;