分布式锁的实现
来源:互联网 发布:爱福窝设计软件下载 编辑:程序博客网 时间:2024/06/15 07:04
zookeeper的基础知识
zookeeper原理(转)
zookeeper client使用笔记
Zookeeper实现分布式锁
分布式服务框架 Zookeeper -- 管理分布式环境中的数据
分布式锁的几种实现方式~
Java分布式锁三种实现方案
基于zookeeper的分布锁的实现,存在羊群效应
public class SDistributeLock { private String lockZnode = null; private String lockNameSpace = "/mylock"; private String nodeString = lockNameSpace + "/test1"; private static ZooKeeper zk; private static String zooKeeperUrl="localhost:2181,localhost:2182,localhost:2183"; static { CountDownLatch connectorSemaphore = new CountDownLatch(1); String url = zooKeeperUrl; int timeout = 60000; try { zk = new ZooKeeper(url, timeout, new ConnectWatcher(connectorSemaphore)); System.out.println("++++++++++++++++++"+zk.getState()); connectorSemaphore.await(); System.out.println("++++++++++++++++++"+zk.getState()); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } private void ensureRootPath() throws InterruptedException { try { if (zk.exists(lockNameSpace,true)==null){ zk.create(lockNameSpace,"".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } } catch (KeeperException e) { e.printStackTrace(); } } private void watchNode(final String nodeString, final Thread thread) throws InterruptedException { try { zk.exists(nodeString, new Watcher() { @Override public void process(WatchedEvent watchedEvent) { System.out.println("out "+thread.getName()+"==" + watchedEvent.toString()); if(watchedEvent.getType() == Event.EventType.NodeDeleted){ System.out.println("外部 "+thread.getName()+"==" + "进入中断"); thread.interrupt(); } try { zk.exists(nodeString,new Watcher() { @Override public void process(WatchedEvent watchedEvent) { System.out.println("in "+thread.getName()+ "==" + watchedEvent.toString()); if(watchedEvent.getType() == Event.EventType.NodeDeleted){ System.out.println("内部 "+thread.getName()+"==" + "进入中断"); thread.interrupt(); } try { zk.exists(nodeString,true); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }); } catch (KeeperException e) { e.printStackTrace(); } } /** * 获取锁 * @return * @throws InterruptedException */ public boolean lock() throws InterruptedException { String path = null; ensureRootPath(); watchNode(nodeString,Thread.currentThread()); while (true) { try { path = zk.create(nodeString, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL); } catch (KeeperException e) { System.out.println(Thread.currentThread().getName() + " getting Lock but can not get"); try { Thread.sleep(5000); }catch (InterruptedException ex){ System.out.println("thread is notify"); } } if (!Strings.nullToEmpty(path).trim().isEmpty()) { System.out.println(Thread.currentThread().getName() + " get Lock..."); return true; } } } /** * 释放锁 */ public void unlock(){ try { zk.delete(nodeString,-1); System.out.println(Thread.currentThread().getName() + " release Lock..."); } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } }}
测试类
@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTestpublic class LockTest { @Test public void testDistributeLock(){ ExecutorService executor = Executors.newCachedThreadPool(); final int count = 1000; //在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 final CountDownLatch latch = new CountDownLatch(count); for (int i = 0; i < count; i++) { final DistributeLock node = new DistributeLock(); executor.submit(new Runnable() { public void run() { try { //如果不加CountDownLatch,睡1s会直接让主线程跑完shutdown而没有加锁, // 这里睡1000秒可以让多个线程同时执行 Thread.sleep(1000); node.lock(); // 阻塞获取锁 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { latch.countDown(); node.unlock(); } } }); } try { //await方法调用此方法会一直阻塞当前线程,直到计时器的值为0 latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } executor.shutdown(); }}
执行结果
pool-3-thread-5 get Lock...
pool-3-thread-2 getting Lock but can not get
pool-3-thread-3 getting Lock but can not get
pool-3-thread-1 getting Lock but can not get
pool-3-thread-4 getting Lock but can not get
out pool-3-thread-2==WatchedEvent state:SyncConnected type:NodeCreated path:/mylock/test1
out pool-3-thread-1==WatchedEvent state:SyncConnected type:NodeCreated path:/mylock/test1
out pool-3-thread-5==WatchedEvent state:SyncConnected type:NodeCreated path:/mylock/test1
in pool-3-thread-2==WatchedEvent state:SyncConnected type:NodeDeleted path:/mylock/test1
内部 pool-3-thread-2==进入中断
thread is notify
pool-3-thread-5 release Lock...
in pool-3-thread-1==WatchedEvent state:SyncConnected type:NodeDeleted path:/mylock/test1
内部 pool-3-thread-1==进入中断
thread is notify
pool-3-thread-2 get Lock...
out pool-3-thread-4==WatchedEvent state:SyncConnected type:NodeDeleted path:/mylock/test1
外部 pool-3-thread-4==进入中断
thread is notify
pool-3-thread-1 getting Lock but can not get
in pool-3-thread-3==WatchedEvent state:SyncConnected type:NodeDeleted path:/mylock/test1
内部 pool-3-thread-3==进入中断
thread is notify
pool-3-thread-4 getting Lock but can not get
in pool-3-thread-5==WatchedEvent state:SyncConnected type:NodeDeleted path:/mylock/test1
内部 pool-3-thread-5==进入中断
pool-3-thread-3 getting Lock but can not get
in pool-3-thread-4==WatchedEvent state:SyncConnected type:NodeDeleted path:/mylock/test1
内部 pool-3-thread-4==进入中断
pool-3-thread-2 release Lock...
thread is notify
pool-3-thread-4 get Lock...
pool-3-thread-4 release Lock...
pool-3-thread-1 get Lock...
pool-3-thread-3 getting Lock but can not get
pool-3-thread-1 release Lock...
pool-3-thread-3 get Lock...
解决羊群效应:
public class DistributeLock { private String lockZnode = null; private static ZooKeeper zk; private static String zooKeeperUrl="localhost:2181,localhost:2182,localhost:2183"; static { CountDownLatch connectorSemaphore = new CountDownLatch(1); String url = zooKeeperUrl; int timeout = 60000; try { zk = new ZooKeeper(url, timeout, new ConnectWatcher(connectorSemaphore)); System.out.println("++++++++++++++++++"+zk.getState()); connectorSemaphore.await(); System.out.println("++++++++++++++++++"+zk.getState()); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 获取锁 * @return * @throws InterruptedException */ public void lock(){ try { Stat stat = zk.exists("/locknode", false);//此去不执行 Watcher if(stat == null){ //创建根节点,永久存在 zk.create("/locknode", "lock".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } String path = zk.create("/locknode/guid-lock","lock".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); lockZnode=path; List<String> children = zk.getChildren("/locknode", true); Collections.sort(children); if (!Strings.nullToEmpty(path).trim().isEmpty() &&!Strings.nullToEmpty(children.get(0)).trim().isEmpty() &&path.equals("/locknode/"+children.get(0))) { System.out.println(Thread.currentThread().getName() + " get Lock..."); return; } String watchNode = null; for (int i=children.size()-1;i>=0;i--){ if(children.get(i).compareTo(path.substring(path.lastIndexOf("/") + 1))<0){ watchNode = children.get(i); break; } } if (watchNode!=null){ final String watchNodeTmp = watchNode; //给当前线程的创建的znode小的znode添加监听,当发生删除事件的时候只叫醒当前的这个线程 final Thread thread = Thread.currentThread(); Stat stat1 = zk.exists("/locknode/" + watchNodeTmp,new Watcher() { @Override public void process(WatchedEvent watchedEvent) { if(watchedEvent.getType() == Watcher.Event.EventType.NodeDeleted){ thread.interrupt(); } try { zk.exists("/locknode/" + watchNodeTmp,true); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }); if(stat1 != null){ System.out.println("Thread " + Thread.currentThread().getId() + " waiting for " + "/locknode/" + watchNode); } } try { //等待直到被唤醒 Thread.sleep(1000000000); }catch (InterruptedException ex){ System.out.println(Thread.currentThread().getName() + " notify"); System.out.println(Thread.currentThread().getName() + " get Lock..."); return; } } catch (Exception e) { e.printStackTrace(); } } /** * 释放锁 */ public void unlock(){ try { System.out.println(Thread.currentThread().getName() + "release Lock..."); zk.delete(lockZnode,-1); } catch (InterruptedException e) { e.printStackTrace(); } catch (KeeperException e) { e.printStackTrace(); } }}
源码地址
但是利用zookeeper1的端口去get znode的时候,不管这个znode是在哪个zookeeper服务器下创建的都可以获取到,说明整个zookeeper集群维护的是同一个文件结构
另外在创建节点的时候必须是CreateMode.PERSISTENT_SEQUENTIAL持久化类型的,否则在使用命令获取节点的时候是获取不到的,EPHEMERAL这种类型的数据是停止服务后无法获取的,请求的服务关闭时临时的值也会消失
将APP1的所有配置配置到/APP1 znode下,APP1所有机器一启动就对/APP1这个节点进行监控(zk.exist("/APP1",true)),并且实现回调方法Watcher,那么在zookeeper上/APP1 znode节点下数据发生变化的时候,每个机器都会收到通知,Watcher方法将会被执行,那么应用再取下数据即可(zk.getData("/APP1",false,null));
一个leader,多个follower
每个server保存一份数据副本
全局数据一致
分布式读写
ZooKeeper拥有一个层次的命名空间,这个和分布式的文件系统非常相似。不同的是ZooKeeper命名空间中的Znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分,并可以具有子znode。用户对znode具有增、删、改、查等操作
ZooKeeper目录树中每一个节点对应一个Znode。每个Znode维护着一个属性结构,它包含着版本号(dataVersion),时间戳(ctime,mtime)等状态信息。ZooKeeper正是使用节点的这些特性来实现它的某些特定功能。每当Znode的数据改变时,他相应的版本号将会增加。每当客户端检索数据时,它将同时检索数据的版本号。并且如果一个客户端执行了某个节点的更新或删除操作,他也必须提供要被操作的数据版本号。如果所提供的数据版本号与实际不匹配,那么这个操作将会失败。
ooKeeper允许各分布式进程通过一个共享的命名空间相互联系,该命名空间类似于一个标准的层次型的文件系统:由若干注册了的数据节点构成(用Zookeeper的术语叫znode),这些节点类似于文件和目录。
客户端可以在节点上设置watch(我们称之为监视器)。当节点状态发生改变时(数据的增、删、改)将会触发watch所对应的操作。当watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,因为watch只能被触发一次。
ZK集群搭建
利用zookeeper部署了一个最小的虚拟集群,有3个zookeeper服务器,三个服务器使用的是同一个IP但是不同的端口,写了一个服务多次请求这个集群可以清楚的看到,多次请求分别到了不同的zookeeper服务器上。
但是利用zookeeper1的端口去get znode的时候,不管这个znode是在哪个zookeeper服务器下创建的都可以获取到,说明整个zookeeper集群维护的是同一个文件结构,每个zk节点的数据都是一致的
另外在创建节点的时候必须是CreateMode.PERSISTENT_SEQUENTIAL持久化类型的,否则在使用命令获取节点的时候是获取不到的,EPHEMERAL这种类型的数据是停止服务后无法获取的,请求的服务关闭时临时的值也会消失
- 分布式锁的实现
- 分布式锁的实现
- 分布式锁的实现
- 分布式锁的实现
- 分布式锁的实现
- 分布式锁的简单实现
- 分布式锁的简单实现
- redis分布式锁的实现
- redis实现的分布式锁
- 【转】分布式锁的实现
- 分布式锁的实现方式
- 详解:分布式锁的实现
- 分布式锁的多种实现
- 分布式锁的实现方式
- redis分布式锁的实现
- 分布式锁的redis实现
- 分布式锁的多种实现
- redisson分布式锁的实现
- spring多版本整合
- java:文件流下载文档
- android之解析xml(XmlPullParser)
- SQLi Labs 指南
- POJ3320-Jessica's Reading Problem
- 分布式锁的实现
- bzoj4583 购物
- BZOJ 3275: Number 最小割
- GTK3.0学习第二天---信号(signal)等
- Ajax 与跨域 小结
- javascript 学习一 文件路径
- c中静态函数和普通函数区别
- 如何在阿里云服务器部署Redis第二篇--redis的部署和启动
- RabbitMQ (三) 发布/订阅