Zookeeper实现分布式独占锁

   由于项目是要部署到多个节点上进行运行的,并且没有使用主备模式,使用的是主主模式,所以当两个节点上不同进程操作同一资源的时候,需要一个分布式锁对资源进行加锁处理。

    目前一般主流的方案都是使用redis来实现的,奈何当前项目处理的更多是离线数据而不是实时数据,基于业务考虑当前版本暂时没有把redis引进来,所以只能基于ZK实现一个分布式锁(压力不算太大,ZK还是抗的住的)。

    本来是打算参考github上他人的实现自己写一个的,发下Curator中就有现成的,那就先直接用现成的好了。

 

//todo 插入使用的demo

可以看出来,Curator已经封装的特别好了…

 

InterProcessMutex设计思想:

    InterProcessMutex是Curator实现的一种分布式可重入排他锁。该锁实现的思想大致如下:

 

    所有的线程都去同一个ZK路径下创建临时有序类型节点(EPHEMERAL_SEQUENTIAL,序号是ZK根据当前子节点数量自动添加整数序号)。

    如果线程检测到自身的节点序号最小,表明当前线程获取到了锁。其他节点会在序号比自己小的前一个节点上注册一个Wacth,节点被删除时能够接收到通知,如果发现自己当前的序号是该路径下最小的,表示自己获取到的锁,循环一直不停的执行该流程。

    有两种情况会释放锁,第一种是正常情况下事务处理结束释放锁,第二种是ZK检测到客户端连接超时了,主动将节点给删除了。无论上述哪种情况,客户端都不应当再持有锁了。

    (PS: 为什么不是往最小的节点注册而是往比自己小的前一个节点上注册,这样做是为了避免“羊群效应”,如果有1000个线程都往最小的那个节点上注册Watch,那么锁被释放时,就会触发往1000个线程发送通知,对ZK集群会产生巨大的性能影响和网络冲击。由此也可以看出,这是一把公平锁)

 

InterProcessMutex内部具体实现:

获取锁:

    内部调用internalLock()方法获取锁,如果获取锁期间与ZK服务端之间的连接发生异常,会抛出异常:

    -1和null表示获取锁期间会一直阻塞,当然也可以设置超时时间

 

看一下internalLock()方法:

内部有锁重入逻辑

 

正确获取锁之后会在本地线程做一个记录,然后返回true。真正加锁的地方在attemptLock()方法内部:

 

点到attemptLock()方法内部看下:

createsTheLock()是在ZK路径瞎创建临时节点,注意此时并没有获取到锁,核心其实就是一行代码:

client.create().creatingParentsIfNeeded().withProtection().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(path, lockNodeBytes);

internalLockLoop()才是加锁,它判断自己的节点序号是不是最小,是则返回true,表示获取到锁,否则阻塞等待

driver.getsTheLock用于判断当前节点的序号是不是最小,返回的是前一个节点的序号(如果不是最小的话),还有是否当前节点是序号最小的标识符(true/false),如下所示:

如果当前节点序号是最小的,那么predicateResults。getTheLock()得到的就是true,表明序号最小,可以持有锁,否则回往欠一个节点上注册WATCH,前一个节点锁释放了可以获取锁。

 

 

释放锁:

    如果锁不被当前线程持有,调用release()方法会抛出异常,否则将重入次数减一。如果发现减一之后可重入次数已经变成0了,那么删除ZK上的临时节点(表示锁完全被释放,其他线程可以抢占锁了)。

guaranteed()方法可以保证客户端失联的情况下,ZK服务器也能删除临时节点:

 

 

 

几种异常情况说明:

    1.服务端手动把节点删除了,客户端还不知道,此时客户端还可以重入么???!!!这种算是异常情况,不是这把锁要考虑的内容。

    可以测试一下这种异常情况

这个是线程打印出来的日志,三个连续的“get”是手动删除的结果,说明节点被手动删除这种异常情况不是锁要考虑的

 

    2.客户端和服务端之间的网线被人拔了此时会怎么处理???

    心跳超过一定时间,临时节点就会被删除掉了,这样可以保证ZK服务端没有问题,那么客户端呢?!客户端还是认为自己持有锁么?

    这个进程中的其他线程肯定不能持有锁了,甚至这台服务器上都不能持有这把锁了。

    ------ 这种情况是不是只有客户端宕机了才会发生,一般会不会考虑这种异常情况?!

    还是说这个版本的代码有缺陷...

        <dependency>

            <groupId>org.apache.curator</groupId>

            <artifactId>curator-framework</artifactId>

            <version>2.12.0</version>

        </dependency>

        <dependency>

            <groupId>org.apache.curator</groupId>

            <artifactId>curator-recipes</artifactId>

            <version>2.7.1</version>

        </dependency>

 

 

 

参考:

    《从Paxos到Zookeeper 分布式一致性原理与实践》

    https://www.jianshu.com/p/bc5e24e4f614(Curator客户端创建分析)

    https://www.jianshu.com/p/c2b4aa7a12f1(几种分布式锁的实现方式)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值