Zookeeper分布式锁原理笔记

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;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章