1.zookeeper分布式锁
https://blog.csdn.net/Ripper_q/article/details/104450564
在以上文档的基本上,编写基于zookeeper的分布式锁.
1.Curator框架内部有自定义分布式锁(InterProcessMutex),使用临时顺序节点…这篇文章,开始利用curator框架和zookeeper特性,先手写一个分布式锁,然后再分析curator框架自带的分布式锁.
1.1 自定义分布式锁
package zookeeper.zk;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.CountDownLatch;
@Component
public class DistributedLock {
// 定义zookeeper客户端
@Autowired
private CuratorFramework curatorFramework;
//用于挂起当前请求,并且等待上一个分布式锁释放
private static CountDownLatch zkLockLatch = new CountDownLatch(1);
//分布式锁的总节点名,可根据业务类型取名字
private static final String ZK_LOCK_PROJECT = "Ripper-locks";
//分布式锁节点,此为临时节点,可根据业务类型取名字
private static final String DISTRIBUTED_LOCK = "distributed_lock";
/**
* 初始化锁
*/
@PostConstruct
public void init() {
//使用命名空间
curatorFramework = curatorFramework.
usingNamespace("ZKLocks-Namespace");
try {
if (curatorFramework.checkExists().forPath("/" + ZK_LOCK_PROJECT) == null) {
curatorFramework.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath("/" + ZK_LOCK_PROJECT);
}
//针对zk的分布式锁节点,创建相应的watcher时间监听
addWatcherToLock("/" + ZK_LOCK_PROJECT);
} catch (Exception e) {
System.out.println("zookeeper客户端初始化错误");
e.printStackTrace();
}
}
private void addWatcherToLock(String nodePath) throws Exception {
final PathChildrenCache nodeCache = new PathChildrenCache(curatorFramework, nodePath, true);
nodeCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
nodeCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
if (pathChildrenCacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) {
String path = pathChildrenCacheEvent.getData().getPath();
System.out.println("上一个会话已经释放或该会话已经断开");
//判断删除的是某个业务的锁,比如订单锁,
if (path.contains(DISTRIBUTED_LOCK)) {
System.out.println("释放计数器,让当前请求来获得分布式锁");
zkLockLatch.countDown();
}
}
}
});
}
public void getLock() {
while (true) {
try {
curatorFramework.create().creatingParentsIfNeeded()
//创建临时节点
.withMode(CreateMode.EPHEMERAL)
.withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)
.forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK);
System.out.println("获取分布式锁成功");
return;
} catch (Exception e) {
System.out.println("获取分布式锁失败");
try {
//如果没有获取到锁,需要重新设置同步资源值
if (zkLockLatch.getCount() <= 0) {
zkLockLatch = new CountDownLatch(1);
}
zkLockLatch.await();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
public boolean releaseLock() {
try {
if (curatorFramework.checkExists().forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK) != null) {
curatorFramework.delete().forPath("/" + ZK_LOCK_PROJECT + "/" + DISTRIBUTED_LOCK);
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
System.out.println("分布式锁释放完毕");
return true;
}
}
编写测试代码
//模拟数据库自增1,起始值为1
private AtomicInteger count = new AtomicInteger(1);
/**
*测试
*/
@Test
public void lockTest(){
// 自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,60, TimeUnit.SECONDS,new ArrayBlockingQueue<>(1000));
try {
for(int i=0;i<1000;i++){
executor.execute(new Task());
}
Thread.sleep(1000000);
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* 任务
*/
public class Task implements Runnable {
@Override
public void run() {
// 获取分布式锁
distributedLock.getLock();
//业务逻辑
try {
int i = count.incrementAndGet();
System.out.println(i);
// 模拟业务耗时
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//释放分布式锁
distributedLock.releaseLock();
}
}
代码运行结束
1.2 curator内部分布式锁
InterProcessMutex:分布式可重入排它锁(常用)
InterProcessSemaphoreMutex:分布式排它锁
InterProcessReadWriteLock:分布式读写锁
InterProcessMultiLock:将多个锁作为单个实体管理的容器
分析InterProcessMutex源码
- InterProcessMutex.acquire
//获取分布式锁,阻塞
@Override
public void acquire() throws Exception
{
if ( !internalLock(-1, null) )
{
throw new IOException("Lost connection while trying to acquire lock: " + basePath);
}
}
// 获取分布式锁,超时阻塞
@Override
public boolean acquire(long time, TimeUnit unit) throws Exception
{
return internalLock(time, unit);
}
- InterProcessMutex.internalLock
private boolean internalLock(long time, TimeUnit unit) throws Exception
{
// 获取当前线程
Thread currentThread = Thread.currentThread();
// 获取当前线程的锁对象
LockData lockData = threadData.get(currentThread);
if ( lockData != null )
{
// re-entering 可重入
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;
}
}
- LockInternals.attemptLock
String attemptLock(long time, TimeUnit unit, byte[] lockNodeBytes) throws Exception
{
final long startMillis = System.currentTimeMillis();
final Long millisToWait = (unit != null) ? unit.toMillis(time) : null;
final byte[] localLockNodeBytes = (revocable.get() != null) ? new byte[0] : lockNodeBytes;
int retryCount = 0;
String ourPath = null;
boolean hasTheLock = false;
boolean isDone = false;
while ( !isDone )
{
isDone = true;
try
{
//在当前path下创建临时有序节点
ourPath = driver.createsTheLock(client, path, localLockNodeBytes);
//判断是不是序号最小的节点,如果是返回true,否则阻塞等待
hasTheLock = internalLockLoop(startMillis, millisToWait, ourPath);
}
catch ( KeeperException.NoNodeException e )
{
// gets thrown by StandardLockInternalsDriver when it can't find the lock node
// this can happen when the session expires, etc. So, if the retry allows, just try it all again
if ( client.getZookeeperClient().getRetryPolicy().allowRetry(retryCount++, System.currentTimeMillis() - startMillis, RetryLoop.getDefaultRetrySleeper()) )
{
isDone = false;
}
else
{
throw e;
}
}
}
if ( hasTheLock )
{
return ourPath;
}
return null;
}
- StandardLockInternalsDriver.createsTheLock
@Override
public String createsTheLock(CuratorFramework client, String path, byte[] lockNodeBytes) throws Exception
{
String ourPath;
// 是否存储数据data,创建临时顺序节点EPHEMERAL_SEQUENTIAL
if ( lockNodeBytes != null )
{
ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes);
}
else
{
ourPath = client.create().creatingParentContainersIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path);
}
return ourPath;
}
- LockInternals.internalLockLoop
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();
synchronized(this)
{
try
{
//给比自己小的节点设置监听器
client.getData().usingWatcher(watcher).forPath(previousSequencePath);
if ( millisToWait != null )
{
millisToWait -= (System.currentTimeMillis() - startMillis);
startMillis = System.currentTimeMillis();
//等待超时,超时删除临时节点,超时时间是acquire方法传入的参数
if ( millisToWait <= 0 )
{
doDelete = true; // timed out - delete our node
break;
}
//没有超时,继续等待
wait(millisToWait);
}
else
{
//如果等待时间==null,一直阻塞等待
wait();
}
}
catch ( KeeperException.NoNodeException e )
{
// it has been deleted (i.e. lock released). Try to acquire again
}
}
}
}
}
catch ( Exception e )
{
ThreadUtils.checkInterrupted(e);
doDelete = true;
throw e;
}
finally
{
if ( doDelete )
{
//如果如果抛出异常或超时,都会删除临时节点
deleteOurPath(ourPath);
}
}
return haveTheLock;
}
- LockInternals.getSortedChildren()
public static List<String> getSortedChildren(CuratorFramework client, String basePath, final String lockName, final LockInternalsSorter sorter) throws Exception
{
//获得basePath下所有子节点,进行排序
List<String> children = client.getChildren().forPath(basePath);
List<String> sortedList = Lists.newArrayList(children);
Collections.sort
(
sortedList,
new Comparator<String>()
{
@Override
public int compare(String lhs, String rhs)
{
return sorter.fixForSorting(lhs, lockName).compareTo(sorter.fixForSorting(rhs, lockName));
}
}
);
return sortedList;
}
- StandardLockInternalsDriver.getsTheLock
@Override
public PredicateResults getsTheLock(CuratorFramework client, List<String> children, String sequenceNodeName, int maxLeases) throws Exception
{
// 在排好序的children中排第几
int ourIndex = children.indexOf(sequenceNodeName);
validateOurIndex(sequenceNodeName, ourIndex);
//如果小于maxLeases,就是获取到锁,比如这里maxLeases为1,那么节点就必须是第一个
boolean getsTheLock = ourIndex < maxLeases;
// 如果获取到锁,就返回,没有就获取上一个节点的序号
String pathToWatch = getsTheLock ? null : children.get(ourIndex - maxLeases);
return new PredicateResults(pathToWatch, getsTheLock);
}
- LockInternals.deleteOurPath
private void deleteOurPath(String ourPath) throws Exception
{
try
{
client.delete().guaranteed().forPath(ourPath);
}
catch ( KeeperException.NoNodeException e )
{
// ignore - already deleted (possibly expired session, etc.)
}
}
- InterProcessMutex.release
@Override
public void release() throws Exception
{
/*
Note on concurrency: a given lockData instance
can be only acted on by a single thread so locking isn't necessary
*/
Thread currentThread = Thread.currentThread();
LockData lockData = threadData.get(currentThread);
//如果当前线程没有持有锁,不能释放
if ( lockData == null )
{
throw new IllegalMonitorStateException("You do not own the lock: " + basePath);
}
//重入锁计数减一,如果还大于0,不能释放。直到所有重入业务完成,计数为0才能释放
int newLockCount = lockData.lockCount.decrementAndGet();
if ( newLockCount > 0 )
{
return;
}
if ( newLockCount < 0 )
{
throw new IllegalMonitorStateException("Lock count has gone negative for lock: " + basePath);
}
try
{
internals.releaseLock(lockData.lockPath);
}
finally
{
threadData.remove(currentThread);
}
}
- LockInternals.releaseLock
void releaseLock(String lockPath) throws Exception
{
revocable.set(null);
deleteOurPath(lockPath);
}
- 测试类端点跟踪代码
@Test
public void interProcessMutexTest() throws Exception {
InterProcessMutex mutex = new InterProcessMutex(curatorFramework, "/mutex");
boolean acquire = mutex.acquire(600, TimeUnit.SECONDS); //断点
Thread.sleep(10000);
// 进程关闭,会自动释放锁
mutex.release();
}
- LockInternals.notifyFromWatcher
临时节点监听比当前节点的上一个节点,触发watcher的时候,会唤醒所有等待中的节点.
private synchronized void notifyFromWatcher()
{
notifyAll();
}