Zk实现分布式锁
来源:互联网 发布:python 海森堡模型 编辑:程序博客网 时间:2024/06/05 15:21
Zk实现分布式锁
原理:
在分布式环境中,如果多个server一起访问,可能会造成垃圾数据或重复执行,这时需要对资源(如数据库)限制单server访问,我们可以考虑用到分布式锁。
用zookeeper可以轻松实现。基本思想:
1. 创建虚拟节点
2. 虚拟节点排序 ,获取最小的节点名称
3. 比较当前jvm创建的节点路径和最小的节点名称,如果想当则持有锁,否则等待或退出竞争。
清单 4. 同步锁的关键代码
void getLock() throws KeeperException, InterruptedException{
List<String> list = zk.getChildren(root, false);
String[] nodes = list.toArray(new String[list.size()]);
Arrays.sort(nodes);
if(myZnode.equals(root+"/"+nodes[0])){
doAction();
}
else{
waitForLock(nodes[0]);
}
}
void waitForLock(String lower) throws InterruptedException, KeeperException {
Stat stat = zk.exists(root + "/" + lower,true);
if(stat != null){
mutex.wait();
}
else{
getLock();
}
}
实现:
package com.mylearn.zookeeper.test;
import com.jd.common.util.ArrayUtils;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import java.util.*;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
/**
* Created by IntelliJ IDEA.
* User: yingkuohao
* Date: 13-11-26
* Time: 上午10:54
* CopyRight:360buy
* Descrption:
* 分布式锁 :
* 1. 创建虚拟节点
* 2. 虚拟节点排序 ,获取最小的节点名称
* 3. 比较当前jvm创建的节点路径和最小的节点名称,如果想当则持有锁,否则等待或退出竞争。
* To change this template use File | Settings | File Templates.
*/
public class ZkLock {
private static final int SESSION_TIMEOUT = 10000;
static CountDownLatch countDownLatch = new CountDownLatch(1);
CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
public void run() {
System.out.println("虚拟节点准备完毕,开始抢锁"); //后续线程执行
}
});
private static String parentLockPth = "/lockRoot";
volatile static boolean flag = false;
public static void main(String args[]) {
final Map<String, String> map = new HashMap<String, String>();
map.put("node0", "192.168.192.68:2181");
map.put("node1", "192.168.229.79:2181");
map.put("node2", "192.168.229.80:2181");
final ZkLock zkLock = new ZkLock();
zkLock.creatRootNode(map.get("node0")); //创建lock父节点
checkParentIsOk(map); //校验父节点是否ok
//三个节点开始抢锁
for (int i = 0; i < 3; i++) {
final int finalI = i;
Thread thread = new Thread(new Runnable() {
public void run() {
try {
countDownLatch.await(); //三个节点同时执行
String key = "node" + finalI;
zkLock.createEphemeralNode(map.get(key), finalI);
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
});
thread.start();
}
countDownLatch.countDown();//子线程一起开始
}
/**
* 换一个节点去校验是否父节点已经同步
*
* @param map
*/
private static void checkParentIsOk(Map<String, String> map) {
ZkApi zkApi = new ZkApi(map.get("node1"));
ZooKeeper zooKeeper = zkApi.getZk();
try {
String parentData = new String(zooKeeper.getData(parentLockPth, false, null));
System.out.println(Thread.currentThread().getName() + "parentData=" + parentData);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 创建锁的根节点:lockRoot
*
* @param serverPath
*/
public void creatRootNode(String serverPath) {
ZkApi zkApi = new ZkApi(serverPath);
ZooKeeper zooKeeper = zkApi.getZk();
String parentLockPth = "/lockRoot";
zkApi.createNod(parentLockPth, "lockRootData", ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); //创建一个虚节点
try {
String parentData = new String(zooKeeper.getData(parentLockPth, false, null));
System.out.println(Thread.currentThread().getName() + "parentData=" + parentData);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 创建虚拟节点,然后抢锁,生成的结构如:
* /lockRoot/lock0
* /lockRoot/lock1
* /lockRoot/lock2
*
* @param serverPath
* @param i
*/
public void createEphemeralNode(String serverPath, int i) {
ZkApi zkApi = new ZkApi(serverPath);
String currentPath = "/lock" + i;
String wholePath = parentLockPth + currentPath; //节点路径 ,,形如/lockRoot/lock1
String testData = "lockdata"; // 节点初始化数据
List<ACL> aclList = ZooDefs.Ids.OPEN_ACL_UNSAFE; //节点的权限
// zkApi.createNod(path, testData, aclList, CreateMode.EPHEMERAL_SEQUENTIAL); //创建一个带顺序的虚节点成功后会带有一个序号,如lock1000000018, lock1000000019
zkApi.createNod(wholePath, testData, aclList, CreateMode.EPHEMERAL); //创建一个虚节点
try {
cyclicBarrier.await(); //等待三个节点都创建成功后开始抢锁
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
getLock(zkApi.getZk(), parentLockPth, currentPath); //抢锁
}
/**
* 抢锁的核心逻辑
* 1. 创建虚拟节点
* 2. 虚拟节点排序 ,获取最小的节点名称
* 3. 比较当前jvm创建的节点路径和最小的节点名称,如果想当则持有锁,否则等待或退出竞争。
*
* @param zooKeeper
* @param parentLockPth
* @param currentPath
*/
private void getLock(ZooKeeper zooKeeper, String parentLockPth, String currentPath) {
try {
List<String> childNode = zooKeeper.getChildren(parentLockPth, false); //获取孩子列表
Collections.sort(childNode); //把孩子列表排序 ,如:lock0,lock1,lock2
System.out.println(Thread.currentThread().getName() + "孩子节点一共:" + ArrayUtils.join(childNode.toArray(), ","));
String firtNode = childNode.get(0); //获取最小的节点,默认排序是升序,这里获取lock0
System.out.println(Thread.currentThread().getName() + "firNode=" + firtNode + "cureentNode=" + currentPath);
firtNode = "/" + firtNode;
if (currentPath.equals(firtNode)) {
//如果当前节点和第一个孩子节点的顺序相等,则可以获取锁
System.out.println(Thread.currentThread().getName() + "[获取锁ok]" + zooKeeper.getSaslClient().getConfigStatus());
doAction();
zooKeeper.delete(parentLockPth + firtNode, -1);//删除此节点,唤醒其他node
Stat stat = zooKeeper.exists(parentLockPth + firtNode, false);
if (stat == null) {
System.out.println(Thread.currentThread().getName() + "删除虚节点成功");
}
} else {
//如果当前节点的序号不是最小的,则等待获取锁
waitLock(zooKeeper, parentLockPth, currentPath);
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 等待锁,其实真正的来讲,分布式环境下获取不到锁的server就直接退出了,
* 就由获取锁的server去执行任务了。
* @param zooKeeper
* @param parentLockPth
* @param currentPath
*/
private void waitLock(ZooKeeper zooKeeper, String parentLockPth, String currentPath) {
try {
Stat stat = zooKeeper.exists(parentLockPth + currentPath, false);
while (flag = false) {
this.wait(); //flag默认为false,该节点线程一直等待,直至拥有锁的线程修改flag状态,唤醒,
}
if (stat != null) {
//如果获取锁的节点还存在,说明上个节点还在用锁,当前节点只能等待。
System.out.println(Thread.currentThread().getName() + "其他线程正在占用,请等待");
flag = false;
Thread.sleep(2000);
}
System.out.println(Thread.currentThread().getName() + "watiLock.重试,node[0]已不存在");
//如果获取锁的节点不存在了,说明上个节点获取的锁已经释放,它对应的虚节点已经被删除,这时可以继续尝试获取了
getLock(zooKeeper, parentLockPth, currentPath);
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (KeeperException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
private void getLock() {
doAction();
flag = true;
}
private void doAction() {
System.out.println(Thread.currentThread().getName() + "获取锁成功,执行内容");
try {
Thread.currentThread().sleep(2000);
System.out.println(Thread.currentThread().getName() + "睡眠完成");
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
}
结果:
触发了None事件!
节点/lockRoot已存在!
mainparentData=lockRootData
path=192.168.229.79:2181
触发了None事件!
mainparentData=lockRootData
path=192.168.192.68:2181
path=192.168.229.79:2181
path=192.168.229.80:2181
触发了None事件!
触发了None事件!
触发了NodeCreated事件!
创建新节点成功:/lockRoot/lock1
触发了NodeCreated事件!
创建新节点成功:/lockRoot/lock2
触发了None事件!
触发了NodeCreated事件!
创建新节点成功:/lockRoot/lock0
Thread-0孩子节点一共:lock0,lock1,lock2
Thread-0firNode=lock0cureentNode=/lock0
Thread-0[获取锁ok]Will not attempt to authenticate using SASL (无法定位登录配置)
Thread-0获取锁成功,执行内容
Thread-1孩子节点一共:lock0,lock1,lock2
Thread-1firNode=lock0cureentNode=/lock1
Thread-2孩子节点一共:lock0,lock1,lock2
Thread-2firNode=lock0cureentNode=/lock2
Thread-1其他线程正在占用,请等待
Thread-2其他线程正在占用,请等待
Thread-0睡眠完成
Thread-1watiLock.重试,node[0]已不存在
Thread-2watiLock.重试,node[0]已不存在
Thread-1孩子节点一共:lock0,lock1,lock2
Thread-1firNode=lock0cureentNode=/lock1
Thread-2孩子节点一共:lock0,lock1,lock2
Thread-2firNode=lock0cureentNode=/lock2
Thread-1其他线程正在占用,请等待
Thread-2其他线程正在占用,请等待
Thread-0删除虚节点成功
Thread-1watiLock.重试,node[0]已不存在
Thread-2watiLock.重试,node[0]已不存在
Thread-1孩子节点一共:lock1,lock2
Thread-1firNode=lock1cureentNode=/lock1
Thread-1[获取锁ok]Will not attempt to authenticate using SASL (无法定位登录配置)
Thread-1获取锁成功,执行内容
Thread-2孩子节点一共:lock1,lock2
Thread-2firNode=lock1cureentNode=/lock2
Thread-2其他线程正在占用,请等待
Thread-1睡眠完成
Thread-2watiLock.重试,node[0]已不存在
Thread-1删除虚节点成功
Thread-2孩子节点一共:lock2
Thread-2firNode=lock2cureentNode=/lock2
Thread-2[获取锁ok]Will not attempt to authenticate using SASL (无法定位登录配置)
Thread-2获取锁成功,执行内容
Thread-2睡眠完成
Thread-2删除虚节点成功
可见,程序最开始三个线程模拟三个节点,先各自创建自己的虚拟节点:lock0,lock1,lock2.然后通过CyclicBarrier来控制同时抢锁,很显然,lock0会成功,lock1和lock2都会阻塞;之后lock0执行完任务后会释放锁,lock1,lock2被唤醒再次去竞争锁,这时候lock1获胜,lock2继续阻塞;lock1执行完毕后释放,剩下lock2.此时lock2也可以获取锁了。
参考:
https://www.ibm.com/developerworks/cn/opensource/os-cn-zookeeper/
- Zk实现分布式锁
- zk实现分布式锁
- zk实现分布式锁
- zk实现分布式锁
- 利用ZK实现分布式锁
- 5.zk实现-分布式排它锁
- ZK-分布式锁
- zk实现分布式统一配置管理
- zk-zclient01 监听、分布式锁
- zk实现锁
- dubbo+zk实现demo
- 使用zk实现主从选举
- zk
- zk
- zk
- ZK
- zk
- zK
- FTP的上传下载工具类
- Android AsyncTask运作原理和源码分析
- Oracle开发专题之:分析函数总结
- 菜鸟玩qt(2)---翻译QSqlTableModel Class帮助文档
- so young so 嫩 - 25 吸取教训
- Zk实现分布式锁
- 图片处理系列三Android多点触控对图片自由缩放和移动
- sql语句还原db2数据库
- .net随机数
- DL的相关资源(持续补充)
- WinRAR命令行参数整理
- 让PC标签 num 参数支持变量的方法
- 考试系统——数据库之合并动态生成表
- UITable 过滤