一、分布式锁方案比较
利用 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分布式锁的实现的详细内容...