好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

分析ZooKeeper分布式锁的实现

一、分布式锁方案比较

方案 实现思路 优点 缺点
利用 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分布式锁的资料请关注其它相关文章!

原文链接:https://HdhCmsTestcnblogs测试数据/itwxe/p/14948383.html

查看更多关于分析ZooKeeper分布式锁的实现的详细内容...

  阅读:15次