当前位置: 首页 > news >正文

Zookeeper是什么?基于zookeeper实现分布式锁

zookeeper听的很多,但实际在应用开发中用的不错,主要是作为中间件配合使用的,例如:Kafka。

了解zk首先需要知道它的数据结构,可以想象为树、文件夹目录。每个节点有基本的信息,例如:创建时间、修改时间、版本,数据长度等。另外节点可以设置data,也就是数据,以字节的方式进行插入/获取,另外节点还拥有权限和状态。

状态很关键,有持久、临时(会话级别)、持久+顺序、临时+顺序、持久+TTL、临时+TTL。

顺序是给同一个节点增加一个编号,例如:path:/distributed_locks/lock

插入多个,在zk中是:/distributed_locks/lock0000000001和/distributed_locks/lock0000000002、、。

到这里数据结构已经大致清楚了,那么zk存在的意义是什么?

首先,zk的定义:是一个集中式服务,用于维护配置信息、命名、提供分布式同步和提供组服务。

关键点:集中、分布式。

在程序进行分布式、多节点部署时,传统程序内存中的变量或者锁机制等都不能在多节点中进行通用。此时,就需要一个集中式的一个中间件,在中间件上存储我们需要同时方案的变量或者其他定义。

那么,我们为什么不直接使用db数据库呢,可能是因为重?也可能是一些特殊的功能db中并不能实现?(临时会话、TTL?)。

作为目前很火热的一个中间件,存在它的意义肯定是有的。为什么说呢,zk是Java实现的,与 Hadoop、Kafka 等 Java 生态项目无缝集成。同理,可以想象,每个语言的特性不一致,都会有不同的中间件或者包。

上述,基本都是个人的一些理解,希望能给大家带来点启发。

zookeeper,咱们的扩展功能到分布式锁这里。通过节点的特性,我们采用会话级别、顺序性质的节点进行实现。

当我们的线程需要去尝试获取锁时,连接zk肯定是个会话,同时zk会根据顺序将不同的线程进行排序,线程内部只需要轮询、wait/notify等方式判断是否轮到自己得到锁了。获取到锁后,执行业务逻辑之后,随之可以将锁进行释放,以便让另外一个线程得到锁。

代码实现用2种方式实现:

原生zookeeper方法实现

package com.fahe.testdistrubutedlock.zk;
​
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
​
import java.util.List;
import java.util.concurrent.CountDownLatch;
​
/*** @program: test-distrubuted-lock* @description: client* @author: <linfahe-694204477@qq.com>* @create: 2025-04-23 14:05**/
@Slf4j
public class ZkClient implements Watcher {
​public static final String ZK_ADDR = "127.0.0.1:32181";public ZooKeeper zk;public CountDownLatch connectedSignal = new CountDownLatch(1);
​public ZkClient() {try {zk = new ZooKeeper(ZK_ADDR, 3000, this);connectedSignal.await(); // 等待连接成功} catch (Exception e) {throw new RuntimeException(e);}}
​@Overridepublic void process(WatchedEvent watchedEvent) {log.info("process WatchedEvent : {}", watchedEvent);if (Event.KeeperState.SyncConnected == watchedEvent.getState()) {connectedSignal.countDown();}}
​
​// 创建持久节点public void createNode() throws KeeperException, InterruptedException {Stat existsed = zk.exists("/my-node", false);if (existsed != null) {
//            zk.delete("/my-node", -1);return;}String path = zk.create("/my-node", "data".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);System.out.println("创建节点:" + path);}
​// 获取节点数据public void getData() throws KeeperException, InterruptedException {byte[] data = zk.getData("/my-node", false, null);System.out.println("节点数据:" + new String(data));}
​public static void main(String[] args) throws InterruptedException, KeeperException {ZkClient zkClient = new ZkClient();List<String> children = zkClient.zk.getChildren("/", true);for (String child : children) {log.info("child : {}", child);}zkClient.createNode();zkClient.getData();}
​public void close() {try {if (zk != null) {zk.close();}} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
​
package com.fahe.testdistrubutedlock.zk;
​
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
​
import java.util.Collections;
import java.util.List;
​
public class DistributedLock {private static final String LOCK_ROOT = "/locks";private static final String LOCK_NODE = LOCK_ROOT + "/lock_";private ZooKeeper zooKeeper;private String lockPath;
​public DistributedLock(ZooKeeper zooKeeper) throws Exception {this.zooKeeper = zooKeeper;Stat stat = zooKeeper.exists(LOCK_ROOT, false);if (stat == null) {zooKeeper.create(LOCK_ROOT, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);}}
​public void acquireLock() throws Exception {lockPath = zooKeeper.create(LOCK_NODE, "new byte[0]".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);System.out.println("Lock path: " + lockPath);
​while (true) {List<String> children = zooKeeper.getChildren(LOCK_ROOT, false);Collections.sort(children);String smallestChild = LOCK_ROOT + "/" + children.get(0);
​if (lockPath.equals(smallestChild)) {System.out.println("Acquired lock: " + lockPath);return;}System.out.println("Waiting for lock: " + lockPath + "; smallestChild : " + smallestChild);String watchNode = null;for (int i = children.size() - 1; i >= 0; i--) {String child = LOCK_ROOT + "/" + children.get(i);if (child.compareTo(lockPath) < 0) {watchNode = child;break;}}System.out.println("Waiting for lock: " + lockPath + "; smallestChild : " + smallestChild + " ; watchNode = " + watchNode);
​if (watchNode != null) {final Object lock = new Object();Watcher watcher = new Watcher() {@Overridepublic void process(WatchedEvent event) {synchronized (lock) {lock.notifyAll();}}};
​Stat stat = zooKeeper.exists(watchNode, watcher);if (stat != null) {synchronized (lock) {lock.wait();}}}}}
​public void releaseLock() throws Exception {if (lockPath != null) {zooKeeper.delete(lockPath, -1);System.out.println("Released lock: " + lockPath);lockPath = null;}}
​public static void main(String[] args) {ZkClient client = new ZkClient();// 模拟多线程。for (int i = 0; i < 30; i++) {new Thread(() -> {try {mainTest(client);} catch (Exception e) {e.printStackTrace();}}).start();}// 模拟多实例。ZkClient client2 = new ZkClient();for (int i = 0; i < 30; i++) {new Thread(() -> {try {mainTest(client2);} catch (Exception e) {e.printStackTrace();}}).start();}}
​public static void mainTest(ZkClient client) {
//         = new ZkClient();try {ZooKeeper zooKeeper = client.zk;
​DistributedLock lock = new DistributedLock(zooKeeper);lock.acquireLock();System.out.println("Lock acquired");// 模拟业务逻辑int randomSleepTime = (int) (Math.random() * 100);System.out.println("randomSleepTime = " + randomSleepTime);Thread.sleep(randomSleepTime);System.out.println("Business logic completed");lock.releaseLock();
//            client.close();} catch (Exception e) {e.printStackTrace();}}
}
​

使用Curator三方包实现:

package com.fahe.testdistrubutedlock.zk;
​
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;
​
​
/*** @program: test-distrubuted-lock* @description: curator 测试* @author: <linfahe-694204477@qq.com>* @create: 2025-04-23 15:04**/
public class CuratorMain {private final InterProcessMutex lock;private static final String LOCK_PATH = "/distributed_lock/my_lock";private static final String ZK_ADDR = "127.0.0.1:32181";
​public CuratorMain() {CuratorFramework client = CuratorFrameworkFactory.newClient(ZK_ADDR,new ExponentialBackoffRetry(200, 2));client.start();this.lock = new InterProcessMutex(client, LOCK_PATH);}
​public boolean acquireLock() {try {lock.acquire();return true;} catch (Exception e) {e.printStackTrace();return false;}}
​public void releaseLock() {try {if (lock.isAcquiredInThisProcess()) {lock.release();}} catch (Exception e) {e.printStackTrace();}}
​public static void main(String[] args) {CuratorMain curatorMain = new CuratorMain();for (int i = 0; i < 100; i++) {new Thread(() -> {boolean acquireLock = curatorMain.acquireLock();System.out.println("thread-" + Thread.currentThread().getName() + " is running");System.out.println("acquireLock = " + acquireLock);if (acquireLock) {curatorMain.releaseLock();}}, "thread-" + i).start();}CuratorMain curatorMain2 = new CuratorMain();for (int i = 100; i < 200; i++) {new Thread(() -> {boolean acquireLock = curatorMain2.acquireLock();System.out.println("thread-" + Thread.currentThread().getName() + " is running");System.out.println("acquireLock = " + acquireLock);if (acquireLock) {curatorMain2.releaseLock();}}, "thread-" + i).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}
}
​

相关文章:

  • UML设计系列(9):开发过程中如何应用UML
  • MATLAB 下载安装教程
  • C# 文件读取
  • Elasticsearch 堆内存使用情况和 JVM 垃圾回收
  • 【初级】前端开发工程师面试100题(一)
  • DB-Day11笔记-函数和存储过程面试题讲解
  • VMware Fusion Pro 13 Mac版虚拟机 安装Win11系统教程
  • 《巧用DeepSeek快速搞定数据分析》书籍分享
  • 《k230-AI_DEMO》车牌识别
  • 基于PaddleOCR对图片中的excel进行识别并转换成word优化(二)
  • 如何使用LangChain调用Ollama部署的模型?
  • 国产RK3568+FPGA以 ‌“实时控制+高精度采集+灵活扩展”‌ 为核心的解决方案
  • BM1684X+FPGA+GMSL视觉解决方案:驱动工业智能化升级的核心引擎
  • 在Linux驱动开发中使用DeepSeek的方法
  • 0101基础知识-区块链-web3
  • webrtc建立连接的过程
  • OpenRAN 6G网络:架构、用例和开放问题
  • 【AI】Windows环境安装SPAR3D单图三维重建心得
  • c语言之杂识
  • 安装jdk报错2503、2502--右键msi文件没法管理员权限执行(Windows解决方案)
  • 上海市进一步支持汽车置换更新!一图读懂补贴政策实施细则
  • 对话地铁读书人|中学教师董女士:借来的书更好看
  • 蚌埠一动物园用染色犬扮熊猫引争议,园方回应:被投诉已撤走
  • 透纳仍是英国最好的艺术家,浦东美术馆有他的画展
  • 十二届上海市委第六轮巡视启动,对18家市管单位开展常规巡视
  • 人民网评“我愿意跟他挨着”热搜第一:充满温暖力量的七个字