ZooKeeper排它锁
排它锁(Exclusive Lock),又称为写锁和独占锁。在Java中排它锁可以用synchronized机制和JDK5提供的ReentrantLock。ZooKeeper中直接的API的实现排它锁,而是通过ZooKeeper的节点来实现锁。首先创建一个/Exclusive_Lock节点,然后创建一个其子节点 /Exclusive_Lock/lock,为临时(EPHEMERAL)节点。如下图
锁获取
需要获取锁的所有Client,都试图创建临时节点 /Exclusive_Lock/lock,ZK会保证只有一个client创建成功,创建成功的client获得锁。没有获得锁的节点需要到/Exclusive_Lock节点下面注册节点变更的Watcher监听,一旦lock节点变更,其他的client可以再次创建lock节点(抢占锁)。
锁释放
锁的释放,即是lock节点被删除,所以有两种情况可以释放锁
- 持有锁的client正常完成业务,主动删除临时节点/Exclusive_Lock/lock
- 持有锁的client发生宕机或者Session过期, ZK服务器会删除临时节点/Exclusive_Lock/lock
流程图
排它锁实现
1 | public class DistributedExcusiveLock implements Watcher{ |
ZooKeeper共享锁
共享锁(Shared Locks),又称为读锁或乐观锁。在Java编程中单机共享锁很容易实现,但是分布式环境下的就不好实现了,基于ZooKeeper很容易实现这个功能。共享锁不同于排它锁在于,排它锁只能被一个线程或者Client持有,即是这两个线程都是读的。
ZooKeeper共享锁原理
Client在ZK服务器创建一个EPHEMERAL_SEQUENTIAL节点,例如/Shared_Lock/192.168.0.1-R-00001,其中R表示读类型,W表示写类型,00001是SEQUENTIAL产生的编号。
- 调用exists()方法监听/hared_Lock下节点的变化
- 调用getChildren()方法获取当前节点列表
- 读请求:如果列表中最小的节点是自己创建的,或者比自己编号的节点都是读的,那么获得锁;
- 写请求:如果列表中最小的节点是自己创建的,那么获得锁;
锁释放
共享锁的释放和排它锁的一样,在正常完成业务后删除节点,或者Client宕机、Session过期等异常
共享锁流程图
共享锁的实现
1 | public class DistributedSharedLock implements Watcher{ |
Apache Curator
Curator是Netflix公司开源的一个Zookeeper客户端,与Zookeeper提供的原生客户端相比,提供了一套Fluent风格的操作API,Curator的抽象层次更高,简化了Zookeeper客户端的开发量。
基于Curator实现一个分布式锁