一、分布式锁方案比较
| 方案 | 实现思路 | 优点 | 缺点 |
|---|---|---|---|
| 利用 mysql 的实现方案 | 利用数据库自身提供的锁机制实现,要求数据库支持行级锁 | 实现简单 | 性能差,无法适应高并发场景;容易出现死锁的情况;无法优雅的实现阻塞式锁 |
| 利用 redis 的实现方案 | 使用 setnx 和 lua 脚本机制实现,保证对缓存操作序列的原子性 | 性能好 | 实现相对复杂,有可能出现死锁;无法优雅的实现阻塞式锁 |
| 利用 zookeeper 的实现方案 | 基于 zookeeper 节点特性及 watch 机制实现 | 性能好,稳定可靠性高,能较好地实现阻塞式锁 | 实现相对复杂 |
二、zookeeper实现分布式锁
这里使用 zookeeper 来实现分布式锁,以50个并发请求来获取订单编号为例,描述两种方案,第一种为基础实现,第二种在第一种基础上进行了优化。
2.1、方案一
流程描述:
具体代码:
ordernumgenerator:
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/**
* @description 生成随机订单号
*/
public class ordernumgenerator {
private static long count = 0;
/**
* 使用日期加数值拼接成订单号
*/
public string getordernumber() throws exception {
string date = datetimeformatter.ofpattern("yyyymmddhhmmss").format(localdatetime.now());
string number = new decimalformat("000000").format(count++);
return date + number;
}
}
|
lock:
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/**
* @description 自定义锁接口
*/
public interface lock {
/**
* 获取锁
*/
public void getlock();
/**
* 释放锁
*/
public void unlock();
}
|
abstractlock:
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
/**
* @description 定义一个模板,具体的方法由子类来实现
*/
public abstract class abstractlock implements lock {
/**
* 获取锁
*/
@override
public void getlock() {
if (trylock()) {
system.out.println("--------获取到了自定义lock锁的资源--------");
} else {
// 没拿到锁则阻塞,等待拿锁
waitlock();
getlock();
}
}
/**
* 尝试获取锁,如果拿到了锁返回true,没有拿到则返回false
*/
public abstract boolean trylock();
/**
* 阻塞,等待获取锁
*/
public abstract void waitlock();
}
|
zookeeperabstractlock:
?
|
1
2
3
4
5
6
7
8
9
10
11
|
/**
* @description 定义需要的服务连接
*/
public abstract class zookeeperabstractlock extends abstractlock {
private static final string server_addr = "192.168.182.130:2181,192.168.182.131:2181,192.168.182.132:2181";
protected zkclient zkclient = new zkclient(server_addr);
protected static final string path = "/lock";
}
|
zookeeperdistrbutelock:
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
/**
* @description 真正实现锁的细节
*/
public class zookeeperdistrbutelock extends zookeeperabstractlock {
private countdownlatch countdownlatch = null;
/**
* 尝试拿锁
*/
@override
public boolean trylock() {
try {
// 创建临时节点
zkclient.createephemeral(path);
return true;
} catch (exception e) {
// 创建失败报异常
return false;
}
}
/**
* 阻塞,等待获取锁
*/
@override
public void waitlock() {
// 创建监听
izkdatalistener izkdatalistener = new izkdatalistener() {
@override
public void handledatachange(string s, object o) throws exception {
}
@override
public void handledatadeleted(string s) throws exception {
// 释放锁,删除节点时唤醒等待的线程
if (countdownlatch != null) {
countdownlatch.countdown();
}
}
};
// 注册监听
zkclient.subscribedatachanges(path, izkdatalistener);
// 节点存在时,等待节点删除唤醒
if (zkclient.exists(path)) {
countdownlatch = new countdownlatch(1);
try {
countdownlatch.await();
} catch (interruptedexception e) {
e.printstacktrace();
}
}
// 删除监听
zkclient.unsubscribedatachanges(path, izkdatalistener);
}
/**
* 释放锁
*/
@override
public void unlock() {
if (zkclient != null) {
system.out.println("释放锁资源");
zkclient.delete(path);
zkclient.close();
}
}
}
|
测试效果:使用50个线程来并发测试zookeeper实现的分布式锁
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/**
* @description 使用50个线程来并发测试zookeeper实现的分布式锁
*/
public class orderservice {
private static class ordernumgeneratorservice implements runnable {
private ordernumgenerator ordernumgenerator = new ordernumgenerator();;
private lock lock = new zookeeperdistrbutelock();
@override
public void run() {
lock.getlock();
try {
system.out.println(thread.currentthread().getname() + ", 生成订单编号:" + ordernumgenerator.getordernumber());
} catch (exception e) {
e.printstacktrace();
} finally {
lock.unlock();
}
}
}
public static void main(string[] args) {
system.out.println("----------生成唯一订单号----------");
for (int i = 0; i < 50; i++) {
new thread(new ordernumgeneratorservice()).start();
}
}
}
|
2.2、方案二
方案二在方案一的基础上进行优化,避免产生“羊群效应”,方案一一旦临时节点删除,释放锁,那么其他在监听这个节点变化的线程,就会去竞争锁,同时访问 zookeeper,那么怎么更好的避免各线程的竞争现象呢,就是使用临时顺序节点,临时顺序节点排序,每个临时顺序节点只监听它本身的前一个节点变化。
流程描述:
具体代码
具体只需要将方案一中的 zookeeperdistrbutelock 改变,增加一个 zookeeperdistrbutelock2,测试代码中使用 zookeeperdistrbutelock2 即可测试,其他代码都不需要改变。
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
/**
* @description 真正实现锁的细节
*/
public class zookeeperdistrbutelock2 extends zookeeperabstractlock {
private countdownlatch countdownlatch = null;
/**
* 当前请求节点的前一个节点
*/
private string beforepath;
/**
* 当前请求的节点
*/
private string currentpath;
public zookeeperdistrbutelock2() {
if (!zkclient.exists(path)) {
// 创建持久节点,保存临时顺序节点
zkclient.createpersistent(path);
}
}
@override
public boolean trylock() {
// 如果currentpath为空则为第一次尝试拿锁,第一次拿锁赋值currentpath
if (currentpath == null || currentpath.length() == 0) {
// 在指定的持久节点下创建临时顺序节点
currentpath = zkclient.createephemeralsequential(path + "/", "lock");
}
// 获取所有临时节点并排序,例如:000044
list<string> childrenlist = zkclient.getchildren(path);
collections.sort(childrenlist);
if (currentpath.equals(path + "/" + childrenlist.get(0))) {
// 如果当前节点在所有节点中排名第一则获取锁成功
return true;
} else {
int wz = collections.binarysearch(childrenlist, currentpath.substring(6));
beforepath = path + "/" + childrenlist.get(wz - 1);
}
return false;
}
@override
public void waitlock() {
// 创建监听
izkdatalistener izkdatalistener = new izkdatalistener() {
@override
public void handledatachange(string s, object o) throws exception {
}
@override
public void handledatadeleted(string s) throws exception {
// 释放锁,删除节点时唤醒等待的线程
if (countdownlatch != null) {
countdownlatch.countdown();
}
}
};
// 注册监听,这里是给排在当前节点前面的节点增加(删除数据的)监听,本质是启动另外一个线程去监听前置节点
zkclient.subscribedatachanges(beforepath, izkdatalistener);
// 前置节点存在时,等待前置节点删除唤醒
if (zkclient.exists(beforepath)) {
countdownlatch = new countdownlatch(1);
try {
countdownlatch.await();
} catch (interruptedexception e) {
e.printstacktrace();
}
}
// 删除对前置节点的监听
zkclient.unsubscribedatachanges(beforepath, izkdatalistener);
}
/**
* 释放锁
*/
@override
public void unlock() {
if (zkclient != null) {
system.out.println("释放锁资源");
zkclient.delete(currentpath);
zkclient.close();
}
}
}
|
以上就是分析zookeeper分布式锁的实现的详细内容,更多关于zookeeper分布式锁的资料请关注快网idc其它相关文章!
原文链接:https://www.cnblogs.com/itwxe/p/14948383.html
相关文章
猜你喜欢
- ASP.NET本地开发时常见的配置错误及解决方法? 2025-06-10
- ASP.NET自助建站系统的数据库备份与恢复操作指南 2025-06-10
- 个人网站服务器域名解析设置指南:从购买到绑定全流程 2025-06-10
- 个人网站搭建:如何挑选具有弹性扩展能力的服务器? 2025-06-10
- 个人服务器网站搭建:如何选择适合自己的建站程序或框架? 2025-06-10
TA的动态
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
您的支持,是我们最大的动力!
热门文章
-
2025-05-25 2
-
2025-05-27 20
-
2025-05-27 81
-
2025-06-04 58
-
2025-06-04 33
热门评论



