分布式锁 ----zookeeper实践 (排它锁)
排它锁概念:
Exclusive Locks,被称为X锁,写锁,独占锁.如果事物T1对数据对象O1加上了排它锁,那么在整个加锁期间,只允许事务T1对O1进行读写操作,其他事务必须等到T1释放锁后才能进行操作.在单机环境中,JDK提供了synchronized关键字和ReentrantLock
重用锁来提供排它锁的功能.
zookeeper实现排它锁原理:
在需要获取排它锁时,所有的客户端都会调用create方法在固定路径下创建节点,并发环境下,只有一个客户端可以创建成功,相当于获取了锁,当该客户端完成事务后,删除该节点.对于没有获取成功的其他节点,则在该路径上设置监听,如果该路径子节点删除事件触发,继续尝试获取锁(create).
具体实现:
定义锁的接口:(我想实现的几个方法)
void lock();
boolean isLocked();
boolean tryLock();
boolean tryLock(long timeout);
void unlock();
直接上代码:
DistributedLock类中拥有的成员变量:
private static CuratorFramework client = null;
private static Logger logger = Logger.getLogger(DistributedLock.class);
protected static CountDownLatch latch = new CountDownLatch(1);
client:为了之后操作zk设置
logger:打印测试日志
latch:lock中要循环等待获取锁,属于一方释放锁和其他再次尝试获取锁之间的沟通桥梁
初始化方法:
public static synchronized void init(String connectString) {
if (client != null)
return; RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
client = CuratorFrameworkFactory.builder().connectString(connectString)
.sessionTimeoutMs(10000).retryPolicy(retryPolicy)
.namespace("LockService").build();
client.start(); // 创建锁目录
try {
if (client.checkExists().forPath("/ExclusiveLockDemo") == null) {
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.withACL(Ids.OPEN_ACL_UNSAFE)
.forPath("/ExclusiveLockDemo");
}
// 创建锁监听
addChildWatcher("/ExclusiveLockDemo");
} catch (Exception e) {
logger.error("ZK服务器连接不上");
throw new RuntimeException("ZK服务器连接不上");
}
}
init方法里做了以下几个事:
1.初始化client
2.创建根目录
3.加根目录的子节点监听(为了unlock)
监听处理函数:
private static void addChildWatcher(String path) throws Exception {
final PathChildrenCache cache = new PathChildrenCache(client, path,
true);
cache.start(StartMode.POST_INITIALIZED_EVENT);
cache.getListenable().addListener(new PathChildrenCacheListener() {
public void childEvent(CuratorFramework client,
PathChildrenCacheEvent event) throws Exception {
if (event.getType().equals(
PathChildrenCacheEvent.Type.INITIALIZED)) { } else if (event.getType().equals(
PathChildrenCacheEvent.Type.CHILD_ADDED)) { } else if (event.getType().equals(
PathChildrenCacheEvent.Type.CHILD_REMOVED)) {
String path = event.getData().getPath();
System.out.println("收到监听"+path);
if(path.contains("ExclusiveLockDemo")){
logger.info("排他锁,收到锁释放通知");
latch.countDown();
}
} else if (event.getType().equals(
PathChildrenCacheEvent.Type.CHILD_UPDATED)) { } }
});
}
主要监听的是子节点的删除行为,当子节点lock删除时,就代表着有节点释放了锁,同时,应该通知等待获取锁的client发起新一轮的获取锁行为,这里用latch控制,latch.countDown()就是通知他们进行下一轮获取,具体代码可以看lock函数的实现。
lock函数:
public void lock() {
while (true) {
try {
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.withACL(Ids.OPEN_ACL_UNSAFE)
.forPath("/ExclusiveLockDemo/lock");
logger.info("成功获取到锁");
return;// 如果节点创建成功,即说明获取锁成功
} catch (Exception e) {
logger.info("此次获取锁没有成功");
try {
//如果没有获取到锁,需要重新设置同步资源值
if(latch.getCount()<=0){
latch = new CountDownLatch(1);
}
latch.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
logger.error("", e1);
}
}
} }
lock函数:尝试在 “/ExclusiveLockDemo/lock”创建znode,当有多个客户端同时调用lock()时,有且只有一个client可以成功创建,打印“成功获取到锁”,其他client将会进入catch语句,打印“此次获取没有成功”,然后重置latch,进入阻塞状态(latch.await())。唤醒条件是持有锁的client释放锁。因为是阻塞的获取锁,所以整个函数处于while(true)死循环里。直到某个时间点成功创建锁,方可return。
islock()函数:
public boolean isLocked() {
try {
Stat stat = client.checkExists().forPath("/ExclusiveLockDemo/lock");
return stat==null?false:true;
} catch (Exception e) {
e.printStackTrace();
} return false;
}
这个比较简单,就是判断一下制定path是否存在znode,也就是是否已经有client成功获取到锁并且还没有释放。
unlock函数:
public void unlock() {
try {
if (client.checkExists().forPath("/ExclusiveLockDemo/lock") != null) {
client.delete().forPath("/ExclusiveLockDemo/lock");
}
} catch (Exception e) {
e.printStackTrace();
} }
这个也比较简单,只是删除锁对应的znode节点即可,但是有个很严重的问题,这种删除太草率,你可以释放一个原本不是你获取的锁,这是不符合道理的,只有你获取的锁,你才有资格释放它,所以正规的写法应该是获取锁的时候加权限,然后释放的时候先检验权限,不是你的不能删。
public boolean tryLock() {
try {
client.create().creatingParentsIfNeeded()
.withMode(CreateMode.EPHEMERAL)
.withACL(Ids.OPEN_ACL_UNSAFE)
.forPath("/ExclusiveLockDemo/lock");
logger.info("成功获取到锁");
} catch (Exception e) {
logger.info("获取锁失败");
return false;
} return true;
}
仿照可重入锁的trylock,尝试获取锁,非阻塞,能获取就返回true,被占用就返回false
public boolean tryLock(long timeout) {
if(timeout<= 0L) return false;
final long deadline = System.currentTimeMillis() + timeout;
for(;;){
if(tryLock())
return true;
else{
timeout = deadline - System.currentTimeMillis();
if (timeout <= 0L)
return false;
}
}
}
有获取时间限制的trylock,实现起来还是比较简单的,大家一看都能看懂
分布式锁 ----zookeeper实践 (排它锁)的更多相关文章
- ZooKeeper分布式锁简单实践
ZooKeeper分布式锁简单实践 在分布式解决方案中,Zookeeper是一个分布式协调工具.当多个JVM客户端,同时在ZooKeeper上创建相同的一个临时节点,因为临时节点路径是保证唯一,只要谁 ...
- [Java复习] 分布式锁 Zookeeper Redis
一般实现分布式锁都有哪些方式? 使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗? 这两种分布式锁的实现方式哪种效率比较高? 1. Zookeeper 都有哪些使用场 ...
- zookeeper 实现分布式锁zookeeper 使用 Curator 示例监听、分布式锁
下载地址: http://download.csdn.net/download/ttyyadd/10239642
- Redis、Zookeeper实现分布式锁——原理与实践
Redis与分布式锁的问题已经是老生常谈了,本文尝试总结一些Redis.Zookeeper实现分布式锁的常用方案,并提供一些比较好的实践思路(基于Java).不足之处,欢迎探讨. Redis分布式锁 ...
- 面试必问:分布式锁实现之zk(Zookeeper)
点赞再看,养成习惯,微信搜索[三太子敖丙]关注这个互联网苟且偷生的工具人. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的 ...
- Zookeeper 分布式锁 (图解+秒懂+史上最全)
文章很长,而且持续更新,建议收藏起来,慢慢读! 高并发 发烧友社群:疯狂创客圈(总入口) 奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : 极致经典 + 社群大片好评 < Java 高并发 三 ...
- 分布式锁(Zookeeper实现)
分布式锁 分布式锁,这个主要得益于 ZooKeeper 为我们保证了数据的强一致性.锁服务可以分为两类,一个是 保持独占,另一个是 控制时序. 1. 所谓保持独占,就是所有试图来获取这个锁的客户端,最 ...
- Docker 下的Zookeeper以及.ne core 的分布式锁
单节点 1.拉取镜像:docker pull zookeeper 2.运行容器 a.我的容器同一放在/root/docker下面,然后创建相应的目录和文件, mkdir zookeeper cd zo ...
- 整理分布式锁:业务场景&分布式锁家族&实现原理
1.引入业务场景 业务场景一出现: 因为小T刚接手项目,正在吭哧吭哧对熟悉着代码.部署架构.在看代码过程中发现,下单这块代码可能会出现问题,这可是分布式部署的,如果多个用户同时购买同一个商品,就可能导 ...
随机推荐
- HTTP无状态协议理解
TTP协议是无状态协议. 无状态是指协议对于事务处理没有记忆能力.缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大.另一方面,在服务器不需要先前信息时它的应 ...
- 【LeetCode】从排序数组中删除重复项
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成. 示例 1 ...
- Unity_如何判断应用设备内存小于1G(内容可定制为根据机器配置进行不同LOD)
直接上脚本,需要用的时候在需要的地方调用就好. 如: //获取设别的最大内存,作为判断LOD等级和决定1G以下设备不能进游戏 #if UNITY_ANDROID && !UNITY_E ...
- VUE 元素拖拽、移动
元素拖拽 作者:一粒尘土 时间:2019-10-30 使用范围:两个元素位置交换,移动元素到指定位置 涉及函数 属性 解释 draggable 是否允许元素进行拖拽 dragstart 拖拽开始触发的 ...
- [转]github 上传project代码
原文地址:https://www.cnblogs.com/f1194361820/p/4741558.html 1)将远程仓库纳入管理 其实就是添加远程仓库,在你已有的本地仓库目录下执行如下命令: $ ...
- 布隆过滤算法体会(BlooomFilter)
在一个m位的位数组里,一个字符串经过k次hash随机分布到k个位置. http://www.cnblogs.com/aspnethot/articles/3442813.html 布隆filter数据 ...
- orm多表查询基于双下划线
###########基于双下划线的跨表查询(基于join实现的)############# key: 正向查询按字段,反向查询按表名小写 1.查询python这本书出版社的名字 ret = Book ...
- python访问aws-S3服务
创建本地 AWS 凭证文件 登录 AWS 管理控制台 并通过以下网址打开 IAM 控制台 https://console.amazonaws.cn/iam/. 创建一个新用户,其权限仅限于您希望您的代 ...
- HTTP/TCP/IP UDP Socket等区别联系
1.TCP连接 手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接.TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上. ...
- LOJ2823 三个朋友 ——查询字串的哈希值
概念 查询字串的hash值 我们所说的哈希通常都是进制哈希,因为它具有一些很方便的性质,例如,具有和前缀和类似的性质. 假设一个字符串的前缀哈希值记为 $h[i]$,进制为 $base$,那么显然 $ ...