文章最前: 我是Octopus,这个名字来源于我的中文名--章鱼;我热爱编程、热爱算法、热爱开源。所有源码在我的个人github ;这博客是记录我学习的点点滴滴,如果您对 Python、Java、AI、算法有兴趣,可以关注我的动态,一起学习,共同进步。
相关文章:
- 分布式系统基础--注册中心Zookeeper初识1
- 分布式系统基础--注册中心Zookeeper相遇2
- 分布式系统基础-- 注册中心Zookeeper应用3
- 分布式系统基础-- 注册中心Zookeeper应用4(分布式锁)
ZooKeeper 是一个分布式协调服务,常用于实现分布式系统中的各种协调任务。分布式锁是其中一个典型的应用场景。在分布式系统中,分布式锁用于控制多个进程或节点对共享资源的访问,确保在同一时间只有一个进程能够访问或修改该资源。
ZooKeeper 分布式锁的实现原理
在 ZooKeeper 中,实现分布式锁通常使用的是临时顺序节点(Ephemeral Sequential Znodes)。具体步骤如下:
-
创建临时顺序节点:
- 每个进程试图获取锁时,都会在指定的节点目录下创建一个临时顺序节点(例如
/lock/lock_0000001
)。 - 这些节点的名称包含了创建顺序,所以不同进程创建的节点有不同的序号。
- 每个进程试图获取锁时,都会在指定的节点目录下创建一个临时顺序节点(例如
-
判断节点的序号:
- 创建节点后,进程会获取这个目录下的所有子节点,并判断自己创建的节点是否是序号最小的。
- 如果是最小序号的节点,说明该进程获得了锁,可以执行对共享资源的操作。
-
监控前一个节点:
- 如果进程创建的节点不是最小序号,它就会找到比自己序号小的上一个节点,并在其上注册一个监听器。
- 当上一个节点被删除(即上一个进程释放锁)时,ZooKeeper 会通知当前进程。
- 当前进程收到通知后,再次检查自己是否成为序号最小的节点,如果是,则获得锁。
-
释放锁:
- 当进程完成对共享资源的操作后,它会删除自己创建的临时顺序节点。
- 由于是临时节点,如果进程因故障断开连接,ZooKeeper 会自动删除该节点,从而释放锁。
ZooKeeper 分布式锁的优点
- 可靠性: 使用临时节点,即使持有锁的进程崩溃,ZooKeeper 也会自动释放锁。
- 公平性: 通过顺序节点,锁的获取顺序是严格按时间顺序来的,不会出现某个进程长时间得不到锁的情况。
前面讲了zookeeper的基础,现在基于zookeeper来实现分布式锁。
基于ZooKeeper分布式锁的流程:
在zookeeper指定节点(locks)下创建跟节点 /locks
获取locks下所有子节点children(孩子节点是临时节点)
通过当前的节点和子节点中最小的节点进行比较,如果相等,表示获得锁成功
监听当前节点的上一个节点
具体实现
1.利用watcher机制手动实现(核心代码)
@Override
public boolean tryLock() {
try {
//创建临时有序节点
CURRENT_LOCK = zk.create(ROOT_LOCK + "/", "0".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println(Thread.currentThread().getName() + "->" +
CURRENT_LOCK + ",尝试竞争锁");
List<String> childrens = zk.getChildren(ROOT_LOCK, false); //获取根节点下的所有子节点
SortedSet<String> sortedSet = new TreeSet();//定义一个集合进行排序
for (String children : childrens) {
sortedSet.add(ROOT_LOCK + "/" + children);
}
String firstNode = sortedSet.first(); //获得当前所有子节点中最小的节点
SortedSet<String> lessThenMe = ((TreeSet<String>) sortedSet).headSet(CURRENT_LOCK); //
if (CURRENT_LOCK.equals(firstNode)) {//通过当前的节点和子节点中最小的节点进行比较,如果相等,表示获得锁成功
return true;
}
if (!lessThenMe.isEmpty()) {
WAIT_LOCK = lessThenMe.last();//获得比当前节点更小的最后一个节点,设置给WAIT_LOCK
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
2.利用curator客户端方式实现(核心代码)
@Override
public void run() {
InterProcessMutex lock = new InterProcessMutex(client, "/distributeLock");
try {
if (lock.acquire(120, TimeUnit.SECONDS)) {
try {
//System.out.println("----" + this.name + "获得资源");
System.out.println("-----" + this.name + "正在处理资源");
Thread.sleep(10 * 1000);
// System.out.println("-----" + this.name + "资源使用完毕-------");
latch.countDown();
} finally {
lock.release();
System.out.println("-------" + this.name + "释放----------");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}