Zookeeper分布式锁原理笔记

    技术2026-08-17  17

    Zookeeper分布式锁原理笔记

    zookeeper以树结构主要保存分布式协调信息状态

    zookeeper简单问题篇跳转我前文8.2章节

    Zookeeper基础

    znode结构 path 唯一路径 childNode 子节点 stat 状态属性,如创建时事务id,判断是否是临时节点等 type 节点类型,4种 data 数据,可有可无 bytes[] create /yun1 "I am data1" get /yun1 会返回节点数据 I am data1 create /yun1/yun2 创建yun1的子节点yun2 ls /yun1 会返回yun1下所有子节点,如yun2 get -s/yun1 返回yun1节点stat状态属性 4种节点类型 持久节点,默认 持久序号节点 临时节点,不能再有子节点,客户端关闭后会删除 临界序号节点,不能再有子节点,客户端关闭后会删除 create -s或-e分别指定使用序号节点和使用临时节点 序号节点:ZK会自动为给定节点名加上一个不断递增数字后缀 临时节点作用场景 yun1节点下有n个机器在执行 yun1会有n个临时子节点,当其中一个机器挂了,这台机器所属临时节点会删除

    zookeeper分布式锁

    场景:假设银行账户读取数据时候,不能修改账户数据 ,账户ID为333

    流程 1 创建临时序号节点 account/333000001,其他人创建则33300000n 2 获取所有序号比自己小的节点 3 是否全部为读节点,是则跳4,否则代表前面有写节点在拿锁则跳5 4 获得锁成功 5 添加子节点更换成监听,监听触发等待获取锁

    1 /333-R000001 获得锁 2 /333-R000002 获得锁 3 /333-W000003 等待锁,监听上一个节点 4 /333-R000004 等待锁,因为读4前面有了写3在等待,监听上一个节点

    为什么设计监听上一个节点?

    如果全让父节点通知所有子节点,会瞬间带来超大并发量,只监听上一个节点避免了这个问题

    实现

    public class Lock{ private String lockId; private String path; private boolean active; 构造函数,get和set方法略过 } public class ZookeeperLock{ //创建临时序号节点 private ZkClient zkClient; public ZookeeperLock(){ zkClient = new ZkClient("192.168.0.149:2181"//连接zk server,5000//session超时时间,20000//连接超时时间); } //获得锁 public Lock lock(String lockId, long timeout){ Lock lockNode = createLockNode(lockId);//创建还没拿锁的新临时节点 lockNode = tryActiveLock(lockNode);//尝试拿锁 if(!lockNode.isActive()//没激活成功,没拿到锁){ try{ synchronized(lockNode){ lockNode.wait(timeout); } }catch(~){ //抛出异常处理 } } return lockNode; } //激活锁 public Lock tryActiveLock(Lock lockNode){ //判断是否获得锁 //按照顺序获得所有排列好的子节点 List<String> list = zkClient.getChildren("/tuling-lock").stream().sorted().map(p->"/tuling-lock/"+p).collect(Collections.toList()); String firstPath = list.get(0); if(firstPath.equals(lockNode.getPath())){ lockNode.setActive(true); }else{//没拿到锁,添加上一个节点监听 String upNodePath = list.get(list.indexOf(lockNode.getPath())-1); //实现对上一个节点监听 zkClient.subscribeDataChanges(upNodePath, new IZkDataListener(){ ~ @Override public void handleDataDeleted(~){ //节点删除了,再次尝试获得锁 Lock lock = tryActiveLock(lockNode); synchronized(lockNode){ if(lock.isActive()){ lockNode.notify();//成功获得锁 } zkClient.unsubscribeDataChanges(~); } } } ) } return lockNode; } //释放锁 public void unlock(Lock lock){ } //创建临时节点 public Lock createLockNode(String lockId){ //创建写锁,这里写死了 String path = zkClient.createEphemeralSequential("/tuling-lock"+lockId//path,"w"//data); Lock lock = new Lock(); lock.setActive(false); lock.setId(lockId); lock.setPath(path); return lock; } }
    Processed: 0.014, SQL: 9