ZK分布式锁(未完 待续)
实现思路
公平锁:创建有序节点,判断本节点是不是序号最小的节点(第一个节点),若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件。
非公平锁:直接尝试在指定path下创建节点,创建成功,则说明该节点抢到锁了。如果创建失败,则监听锁节点的删除事件,或者sleep一段时间后再重试。
可重入:使用ThreadLocal记录加锁客户端的唯一标识。重复时先从ThreadLocal获取,获取到,这认为加锁成功,直接返回。
使用瞬时节点创建可重入公平锁
使用瞬时节点的好处是当Session失效时,该节点将被清理,从而避免使用持久节点加锁成功后,抛异常或宕机或服务器重启等原因造成的锁无法释放问题。
使用curator实现
// 假设需要加锁的订单Id
private static String orderId = "157146671409578219";
// 工程名
private static String appName = "trade_";
// 此次加锁业务处理逻辑描述
private static String operatorDesc = "updateTrade_";
// 部门,每个部门可以有自己的zk空间目录
private static String department = "zfpt" ;
// 锁前缀 应该根据业务 具有唯一性
private static String lockPrefixKey = "/" + appName + operatorDesc ;
/**
* 每个线程 创建自己的Connection ,创建自己的Session
*/
@Test
public void curator() throws Exception {
for (int i = 0 ; i < 100 ; i++) {
Thread currentThread = new Thread(() -> {
// 创建Connection
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("master:2181,slave1:2181,slave2:2181")
.retryPolicy(new RetryOneTime(1000)) //重试策略
.namespace(department) // 可以设置自己部门缩写
.build();
client.start();
// 模拟对同一个订单加锁
InterProcessMutex lock = new InterProcessMutex(client, lockPrefixKey + orderId);
try {
// 一直尝试加锁 直到锁可用。 有点像synchronized
// lock.acquire();
if(lock.acquire(1, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread().getName() + " 抢到锁");
} else {
System.out.println(Thread.currentThread().getName() + "超时没有抢到锁");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 如果当前线程获得到锁,则释放锁
if(lock.isAcquiredInThisProcess()) {
System.out.println(Thread.currentThread().getName() + " 释放锁");
lock.release();
} else {
System.out.println(Thread.currentThread().getName() + " 没有抢到锁,故没有释放锁");
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
currentThread.setName("Lock【" + i + "】");
currentThread.start();
}
Thread.sleep(Integer.MAX_VALUE);
}
实现原理:
因为ZK不允许在临时节点下创建子节点,所以InterProcessMutex工具类会根据加锁传入path的(也就是案例中的lockPrefixKey + orderId)创建一个持久节点;然后在这个持久节点下创建瞬时节点,创建成功后,对该持久节点下的全部子节点进行降序排序,判断当前节点是否是第一个节点,如果是则获取锁,否则对前一个节点加上监听事件。然后Object.wait(),当监听事件被触发,则会调用notifyAll方法,对等待线程进行唤醒。再次尝试获取锁。
缺点:
- 传入的加锁节点会被创建为永久节点(就是lockPrefixKey + orderId),这样zk节点数量将会急速递增。
- 临时节点不稳定:一个客户端加锁成功后,可能会因为网络抖动等原因导致Session断开,该客户端创建的临时节点被清理,导致另外一节正在监听的客户端加锁成功,同时进行操作。
使用持久节点创建可重入公平锁
上文提到的临时节点不稳定,父节点为永久节点无法释放问题。我的拙见是:
- 使用持久节点代替临时节点:释放锁的时候删除自己创建的加锁节点。
- 父节点为永久节点无法释放:可以在每个客户端释放锁的时候进行判断,如果自己是最后一个节点,则删除父节点。
- 但是需要考虑的问题:客户端加锁成功后,宕机或重启或者其他极端异常情况,无法删除加锁节点。最后一个加锁节点同样异常,也无法删除父节点。这时,可以给每个锁加过期时间,过期失效。由于zk没有提供过期自动清理,所以可以在第二次访问该节点的时候 先进行判断,判断失效先删除再创建。如果没有第二次访问的节点 可以依靠定时任务进行节点清理。
ZK分布式锁(未完 待续)的更多相关文章
- Go web编程学习笔记——未完待续
1. 1).GOPATH设置 先设置自己的GOPATH,可以在本机中运行$PATH进行查看: userdeMacBook-Pro:~ user$ $GOPATH -bash: /Users/user/ ...
- 本地锁、redis分布式锁、zk分布式锁
本地锁.redis分布式锁.zk分布式锁 https://www.cnblogs.com/yjq-code/p/dotnetlock.html 为什么要用锁? 大型站点在高并发的情况下,为了保持数据最 ...
- 【分布式锁的演化】终章!手撸ZK分布式锁!
前言 这应该是分布式锁演化的最后一个章节了,相信很多小伙伴们看完这个章节之后在应对高并发的情况下,如何保证线程安全心里肯定也会有谱了.在实际的项目中也可以参考一下老猫的github上的例子,当然代码没 ...
- git安装与使用,未完待续... ...
目录 一.git概念 二.git简史 三.git的安装 四.git结构 五.代码托管中心-本地库和远程库的交互方式 六.初始化本地仓库 七.git常用命令 1.add和commit命令 2.sta ...
- javascript有用小功能总结(未完待续)
1)javascript让页面标题滚动效果 代码如下: <title>您好,欢迎访问我的博客</title> <script type="text/javasc ...
- ASP.NET MVC 系列随笔汇总[未完待续……]
ASP.NET MVC 系列随笔汇总[未完待续……] 为了方便大家浏览所以整理一下,有的系列篇幅中不是很全面以后会慢慢的补全的. 学前篇之: ASP.NET MVC学前篇之扩展方法.链式编程 ASP. ...
- 关于DOM的一些总结(未完待续......)
DOM 实例1:购物车实例(数量,小计和总计的变化) 这里主要是如何获取页面元素的节点: document.getElementById("...") cocument.query ...
- 我的SQL总结---未完待续
我的SQL总结---未完待续 版权声明:本文为博主原创文章,未经博主允许不得转载. 总结: 主要的SQL 语句: 数据操作(select, insert, delete, update) 访问控制(g ...
- virtualbox搭建ubuntu server nginx+mysql+tomcat web服务器1 (未完待续)
virtualbox搭建ubuntu server nginx+mysql+tomcat web服务器1 (未完待续) 第一次接触到 linux,不知道linux的确很强大,然后用virtualbox ...
- MVC丶 (未完待续······)
希望你看了此小随 可以实现自己的MVC框架 也祝所有的程序员身体健康一切安好 ...
随机推荐
- 2016-2017 National Taiwan University World Final Team Selection Contest (Codeforces Gym) 部分题解
D 考虑每个点被删除时其他点对它的贡献,然后发现要求出距离为1~k的点对有多少个. 树分治+FFT.分治时把所有点放一起做一遍FFT,然后减去把每棵子树单独做FFT求出来的值. 复杂度$nlog^ ...
- bzoj 刷题计划~_~
bzoj 2818 两个互质的数的gcd=1,所以他们同时乘一个素数那么他们的gcd=这个素数,所以枚举素数p找n/p以内有多少对互质数,用欧拉函数. bzoj 2809 可并堆,对于每一个子树显然是 ...
- GO_04:GO语言基础条件、跳转、Array和Slice
1. 判断语句if 1. 条件表达式没有括号(这点其他语言转过来的需要注意) 2. 支持一个初始化表达式(可以是并行方式,即:a, b, c := 1, 2, 3) 3. 左大括号必须和条件语句或 e ...
- Maven学习一:使用Myeclipse创建Maven项目
使用Myeclipse2014创建Maven项目有如下几种方式: 1.创建Maven Java项目 1.1 选择新建Maven项目 1.2.选择创建简单项目 1.3.填写项目信息 1.4.创建成功后项 ...
- MyEclipse和Eclipse中jsp、html格式化自动排版问题
一.myeclipse的漂亮排版设置 步骤: 在左侧快捷 “搜索” 框里面输入 html . 点击选中左侧HTML Source . line - width 是设置当前行里面有多少字符时,就换行.这 ...
- ansible安装后运行报错cryptography
[root@kazihuo ~]# ansible all -m ping ERROR! Unexpected Exception, this is probably a bug: (cryptogr ...
- ZeroMQ API(五) 传输模式
1.使用TCP的单播传输:zmq_tcp(7) 1.1 名称 zmq_tcp - 使用TCP的ZMQ单播传输 1.2 概要 TCP是一种无处不在,可靠的单播传输.当通过具有ZMQ的网络连接分布式应用程 ...
- 掌握Markdown
翻译自 https://guides.github.com/features/mastering-markdown/ 转载请注明链接 掌握Markdown Markdown是Github平台上一种轻量 ...
- 四、Kafka 核心源码剖析
一.Kafka消费者源码介绍 1.分区消费模式源码介绍 分区消费模式直接由客户端(任何高级语言编写)使用Kafka提供的协议向服务器发送RPC请求获取数据,服务器接受到客户端的RPC请求后,将数据构造 ...
- OAuth2:Authorization Flows
Ref:http://www.dannysite.com/blog/176/ OAuth2.0协议定义了用于获得授权的四种主要授权类型. 1. Client Credentials 一种基于APP的密 ...