gpt4 book ai didi

分析ZooKeeper分布式锁的实现

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 45 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章分析ZooKeeper分布式锁的实现由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

1、分布式锁方案比较

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

2、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分布式锁的资料请关注我其它相关文章! 。

原文链接:https://www.cnblogs.com/itwxe/p/14948383.html 。

最后此篇关于分析ZooKeeper分布式锁的实现的文章就讲到这里了,如果你想了解更多关于分析ZooKeeper分布式锁的实现的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

45 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com