分析ZooKeeper分布式锁的实现

2025-05-29 0 98

一、分布式锁方案比较

方案 实现思路 优点 缺点
利用 mysql 的实现方案 利用数据库自身提供的锁机制实现,要求数据库支持行级锁 实现简单 性能差,无法适应高并发场景;容易出现死锁的情况;无法优雅的实现阻塞式锁
利用 redis 的实现方案 使用 setnx 和 lua 脚本机制实现,保证对缓存操作序列的原子性 性能好 实现相对复杂,有可能出现死锁;无法优雅的实现阻塞式锁
利用 zookeeper 的实现方案 基于 zookeeper 节点特性及 watch 机制实现 性能好,稳定可靠性高,能较好地实现阻塞式锁 实现相对复杂

二、zookeeper实现分布式锁

这里使用 zookeeper 来实现分布式锁,以50个并发请求来获取订单编号为例,描述两种方案,第一种为基础实现,第二种在第一种基础上进行了优化。

2.1、方案一

流程描述:

分析ZooKeeper分布式锁的实现

具体代码:

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,那么怎么更好的避免各线程的竞争现象呢,就是使用临时顺序节点,临时顺序节点排序,每个临时顺序节点只监听它本身的前一个节点变化。

流程描述:

分析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

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 分析ZooKeeper分布式锁的实现 https://www.kuaiidc.com/104830.html

相关文章

发表评论
暂无评论