zookeeper分布式锁

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内部分布式锁

Lock
InterProcessMutex:分布式可重入排它锁(常用)
InterProcessSemaphoreMutex:分布式排它锁
InterProcessReadWriteLock:分布式读写锁
InterProcessMultiLock:将多个锁作为单个实体管理的容器

分析InterProcessMutex源码
  1. 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);
    }
  1. 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;
    }
}
  1. 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;
    }
  1. 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;
    }
  1. 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;
    }
  1. 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;
    }
  1. 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);
    }
  1. 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.)
        }
    }
  1. 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);
        }
    }
  1. LockInternals.releaseLock
void releaseLock(String lockPath) throws Exception
    {
        revocable.set(null);
        deleteOurPath(lockPath);
    }
  1. 测试类端点跟踪代码
@Test
    public void interProcessMutexTest() throws Exception {
        InterProcessMutex mutex = new InterProcessMutex(curatorFramework, "/mutex");
        boolean acquire = mutex.acquire(600, TimeUnit.SECONDS); //断点
        Thread.sleep(10000);
        // 进程关闭,会自动释放锁
         mutex.release();
    }
  1. LockInternals.notifyFromWatcher
    临时节点监听比当前节点的上一个节点,触发watcher的时候,会唤醒所有等待中的节点.
 private synchronized void notifyFromWatcher()
    {
        notifyAll();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值