本文主要讲述在使用ZooKeeper进行分布式锁的实现过程中,如何有效的避免“羊群效应(herdeffect)”的出现。
最后有实现了代码,仅供参考
本文参考了《Hadoop权威指南》以及以下网页内容实现:
http://aliapp.blog.51cto.com/8192229/1328018
实现过程介绍如下:
一般的分布式锁实现
-
节点的生命周期和客户端会话绑定,即创建节点的客户端会话一旦失效,那么这个节点也会被清除。
-
每个父节点都会负责维护其子节点创建的先后顺序,并且如果创建的是顺序节点(SEQUENTIAL)的话,父节点会自动为这个节点分配一个整形数值,以后缀的形式自动追加到节点名中,作为这个节点最终的节点名。
-
客户端调用create()方法创建名为“_locknode_/guid-lock-”的节点,需要注意的是,这里节点的创建类型需要设置为EPHEMERAL_SEQUENTIAL。
-
客户端调用getChildren(“_locknode_”)方法来获取所有已经创建的子节点,同时在这个节点上注册上子节点变更通知的Watcher。
-
客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,那么就认为这个客户端获得了锁。
-
如果在步骤3中发现自己并非是所有子节点中最小的,说明自己还没有获取到锁,就开始等待,直到下次子节点变更通知的时候,再进行子节点的获取,判断是否获取锁。
问题所在
改进后的分布式锁实现
-
客户端调用create()方法创建名为“_locknode_/guid-lock-”的节点,需要注意的是,这里节点的创建类型需要设置为EPHEMERAL_SEQUENTIAL。
-
客户端调用getChildren(“_locknode_”)方法来获取所有已经创建的子节点,注意,这里不注册任何Watcher。
-
客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点序号最小,那么就认为这个客户端获得了锁。
-
如果在步骤3中发现自己并非所有子节点中最小的,说明自己还没有获取到锁。此时客户端需要找到比自己小的那个节点,然后对其调用exist()方法,同时注册事件监听。
-
之后当这个被关注的节点被移除了,客户端会收到相应的通知。这个时候客户端需要再次调用getChildren(“_locknode_”)方法来获取所有已经创建的子节点,确保自己确实是最小的节点了,然后进入步骤3
代码实现:
package cn.liu.zookeeper.ch14; import java.io.IOException; import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.Watcher.Event.KeeperState; public class ConnectionWatcher implements Watcher { private static final int SESSION_TIMEOUT = 5000; protected ZooKeeper zk; private CountDownLatch connectedSignal = new CountDownLatch(1); public void connect(String hosts) throws IOException, InterruptedException { zk = new ZooKeeper(hosts, SESSION_TIMEOUT, this); connectedSignal.await(); } @Override public void process(WatchedEvent event) { if (event.getState() == KeeperState.SyncConnected) { connectedSignal.countDown(); } } public void close() throws InterruptedException { zk.close(); } }
package cn.liu.zookeeper.project; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.data.Stat; import cn.liu.zookeeper.ch14.ConnectionWatcher; /** * * 本类简单实现了分布式锁的机制 * * 采用单机zookeeper服务器测试;运行多次本机程序,相当于多个客户端用户 * 全部启动完成后,第一个客户端的会话需要手动中断,相当于触发客户端宕机现象 * * * 本类实现的分布式锁避免羊群效应(Herd Effect),具体可详见代码 * * * * * @author Liu Dengtao * * 2014-2-28 */ public class DistributedLock extends ConnectionWatcher { public String join(String groupPath) throws KeeperException, InterruptedException { String path = groupPath + "/lock-" + zk.getSessionId() + "-"; //建立一个顺序临时节点 String createdPath = zk.create(path, null/* data */, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); System.out.println("Created " + createdPath); return createdPath; } /** * 检查本客户端是否得到了分布式锁 * @param groupPath * @param myName * @return * @throws KeeperException * @throws InterruptedException */ public boolean checkState(String groupPath,String myName) throws KeeperException, InterruptedException{ List<String> childList = zk.getChildren(groupPath, false); String[] myStr = myName.split("-"); long myId = Long.parseLong(myStr[2]); boolean minId = true; for (String childName : childList) { String[] str = childName.split("-"); long id = Long.parseLong(str[2]); if (id < myId) { minId = false; break; } } if (minId) { System.out.println(new Date() + "我得到了分布锁,哈哈! myId:" + myId); return true; }else { System.out.println(new Date() + "继续努力吧, myId:" + myId); return false; } } /** * 若本客户端没有得到分布式锁,则进行监听本节点前面的节点(避免羊群效应) * @param groupPath * @param myName * @throws KeeperException * @throws InterruptedException */ public void listenNode(final String groupPath, final String myName) throws KeeperException, InterruptedException{ List<String> childList = zk.getChildren(groupPath, false); String[] myStr = myName.split("-"); long myId = Long.parseLong(myStr[2]); List<Long> idList = new ArrayList<Long>(); Map<Long, String> sessionMap = new HashMap<Long, String>(); for (String childName : childList) { String[] str = childName.split("-"); long id = Long.parseLong(str[2]); idList.add(id); sessionMap.put(id, str[1]+"-"+str[2]); } Collections.sort(idList); int i = idList.indexOf(myId); if (i <=0) { throw new IllegalArgumentException("数据错误!"); } //得到前面的一个节点 long headId = idList.get(i-1); String headPath = groupPath + "/lock-" + sessionMap.get(headId); System.out.println("添加监听:" + headPath); Stat stat = zk.exists(headPath, new Watcher(){ @Override public void process(WatchedEvent event) { System.out.println("已经触发了" + event.getType() + "事件!"); try { while(true){ if (checkState(groupPath,myName)) { Thread.sleep(3000); System.out.println(new Date() + " 系统关闭!"); System.exit(0); } Thread.sleep(3000); } } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }); System.out.println(stat); } public static void main(String[] args) throws Exception { DistributedLock joinGroup = new DistributedLock(); joinGroup.connect("localhost:" + "2181"); //zookeeper的根节点;运行本程序前,需要提前生成 String groupName = "zkRoot"; String memberName = "_locknode_"; String path = "/" + groupName + "/" + memberName; String myName = joinGroup.join(path); if (!joinGroup.checkState(path, myName)) { joinGroup.listenNode(path, myName); } Thread.sleep(Integer.MAX_VALUE); joinGroup.close(); } } // ^^ JoinGroup
相关推荐
zookeeper客户端。分布式锁。 demo。。
基于zookeeper的分布式锁实现demo,可以直接用在生产环境
基于zookeeper的分布式锁简单实现,包含测试代码,实用工具类
zookeeper 分布式锁的实现1
C#基于zookeeper分布式锁的实现源码,参考网上代码,做过一些小调整,目前在项目使用中,运行良好。
zooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是集群的管理者。提供了文件系统和通知机制。...在开发项目的过程中,很多大型项目都是分布式部署的,那么我们现在使用zookeeper实现一个分布式锁。
在分布式高并发的情况下,多个访问之间拥有唯一的一个订单编号,多次提交的订单不会出现重复的,只能是唯一的,ZooKeeper就很好的解决了唯一性
一般除了大公司是自行封装分布式锁框架之外,建议大家用这些开源框架封装好的分布式锁实现,这是一个比较快捷省事儿的方式。 二、ZooKeeper分布式锁机制 接下来我们一起来看看,多客户端获取及释放zk分布式锁的整个...
在程序开发过程中不得不考虑的就是并发问题。在java中对于同一个jvm而言,jdk已经提供了lock和同步等。但是在分布式情况下,往往存在多个进程对一些资源产生竞争...分布式锁顾明思议就是可以满足分布式情况下的并发锁。
利用springboot集成zookeeper,并利用zookeeper实现分布式锁,避免羊群效应
Zookeeper实现分布式锁的Demo Zookeeper实现分布式锁的Demo
此Demo采用zookeeper实现分布式锁,相比于redis实现分布式锁更加可靠
最新版本SpringBoot集成zookeeper实现分布式可重入排他锁实现,支持拓展其他种类锁
只要创建好一个分布式锁,就要不断轮循这个锁什么时候释放。 zk只会对已经创建分布式锁的系统进行创建监听,所以性能消耗很小 2、运行出现错误 redis创建锁的节点如果挂了,那就只能等待超时才能释放锁 zk因为是创建...
解决方案里面包括两个Console项目,一个是ZookeeperNet的基本使用,另一个是用ZookeeperNet实现分布式锁的实例。
主要介绍了java使用zookeeper实现的分布式锁示例,需要的朋友可以参考下
主要介绍了Java(SpringBoot)基于zookeeper的分布式锁实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
利用zookeeper 实现分布式锁
springboot redis zookeeperlock rabbit实现的分布式锁 代码