关键词:
这一节我们主要来看一下zookeeper文件系统的实现。
树结构
为了提高对指定节点的操作,zookeeper使用一个HashMap来存储树结构数据,key为数据路径,value为节点数据。
树节点(DataNode)
1 public class DataNode implements Record 2 //父节点 3 DataNode parent; 4 //节点数据 5 byte data[]; 6 //节点权限 7 Long acl; 8 //节点状态信息 9 public StatPersisted stat; 10 //子节点名 11 private Set<String> children = null; 12 13
数节点状态(StatPersisted)
1 public class StatPersisted implements Record 2 //该节点创建是的事务xid 3 private long czxid; 4 //该节点最后一次修改的事务id 5 private long mzxid; 6 //创建时间 7 private long ctime; 8 //最后一次修改时间 9 private long mtime; 10 //节点版本号 11 private int version; 12 //子节点版本号 13 private int cversion; 14 //acl版本号 15 private int aversion; 16 //是否为零时节点 17 private long ephemeralOwner; 18 //子列表被修改的zxid 19 private long pzxid; 20
配额管理
zookeeper在创建、修改节点时可以设置特定路径上的配额。在实现上,配额也存储在文件系统中,并且还存储了节点当前的信息。配额的控制在特定的路径下:/zookeeper/quota/节点路径/zookeeper_limits 节点的限制数据节点;/zookeeper/quota/节点路径/zookeeper_stats 节点的当前量节点。一个节点路径中只能有一个配额限制。
当在对某个节点进行操作时,我们需要知道该路径下的哪个节点设置了配额,因为树结构使用hashmap来存储,所以不便于通过路径查找,所以使用了一个树结构来表示那个节点上配置了限额。
配额路径(PathTrie)
1 public class PathTrie 2 //根节点 3 private final TrieNode rootNode ; 4 //节点 5 static class TrieNode 6 //是否设置配额,false没有,true有 7 boolean property = false; 8 //子节点 9 final HashMap<String, TrieNode> children; 10 //父节点 11 TrieNode parent = null; 12 13 14 //删除子节点配额 15 void deleteChild(String childName) 16 synchronized(children) 17 if (!children.containsKey(childName)) 18 return; 19 20 TrieNode childNode = children.get(childName); 21 //如果子节点没有自己点直接删除,否则设置property为false 22 if (childNode.getChildren().length == 1) 23 childNode.setParent(null); 24 children.remove(childName); 25 26 else 27 childNode.setProperty(false); 28 29 30 31 32 //新增配额节点 33 public void addPath(String path) 34 if (path == null) 35 return; 36 37 String[] pathComponents = path.split("/"); 38 TrieNode parent = rootNode; 39 String part = null; 40 if (pathComponents.length <= 1) 41 throw new IllegalArgumentException("Invalid path " + path); 42 43 for (int i=1; i<pathComponents.length; i++) 44 part = pathComponents[i]; 45 if (parent.getChild(part) == null) 46 parent.addChild(part, new TrieNode(parent)); 47 48 parent = parent.getChild(part); 49 50 parent.setProperty(true); 51 52 53 //删除配额节点 54 public void deletePath(String path) 55 if (path == null) 56 return; 57 58 String[] pathComponents = path.split("/"); 59 TrieNode parent = rootNode; 60 String part = null; 61 if (pathComponents.length <= 1) 62 throw new IllegalArgumentException("Invalid path " + path); 63 64 for (int i=1; i<pathComponents.length; i++) 65 part = pathComponents[i]; 66 if (parent.getChild(part) == null) 67 return; 68 69 parent = parent.getChild(part); 70 71 TrieNode realParent = parent.getParent(); 72 realParent.deleteChild(part); 73 74 75 //获取指定路径上配额节点最大路径 76 public String findMaxPrefix(String path) 77 if (path == null) 78 return null; 79 80 if ("/".equals(path)) 81 return path; 82 83 String[] pathComponents = path.split("/"); 84 TrieNode parent = rootNode; 85 List<String> components = new ArrayList<String>(); 86 if (pathComponents.length <= 1) 87 throw new IllegalArgumentException("Invalid path " + path); 88 89 int i = 1; 90 String part = null; 91 StringBuilder sb = new StringBuilder(); 92 //最大路径的index 93 int lastindex = -1; 94 while((i < pathComponents.length)) 95 if (parent.getChild(pathComponents[i]) != null) 96 part = pathComponents[i]; 97 parent = parent.getChild(part); 98 components.add(part); 99 if (parent.getProperty()) 100 lastindex = i-1; 101 102 103 else 104 break; 105 106 i++; 107 108 for (int j=0; j< (lastindex+1); j++) 109 sb.append("/" + components.get(j)); 110 111 return sb.toString(); 112 113
监听器管理
zookeeper可以对指定路径进行监听,当指定路径发生变化时,监听器会执行响应的动作。主要是通过将path和watcher建立关联关系,在对指定路径进行操作是调用相应监听器方法。
监听管理器(WatchManager)
1 public class WatchManager 2 //key为path value为该path对应的watcher集合 3 private final HashMap<String, HashSet<Watcher>> watchTable = 4 new HashMap<String, HashSet<Watcher>>(); 5 //key为watcher value为该watcher对应的path集合,使用两个hashmap来维护路径和监听器是因为watcher和路径是多对多关系,这样无论通过watcher还是路径都可以很快找到对应的路径和watcher。 6 private final HashMap<Watcher, HashSet<String>> watch2Paths = 7 new HashMap<Watcher, HashSet<String>>(); 8 9 public synchronized void addWatch(String path, Watcher watcher) 10 //新增watcher到watchTable 11 HashSet<Watcher> list = watchTable.get(path); 12 if (list == null) 13 list = new HashSet<Watcher>(4); 14 watchTable.put(path, list); 15 16 list.add(watcher); 17 //新增watcher到watch2Paths 18 HashSet<String> paths = watch2Paths.get(watcher); 19 if (paths == null) 20 paths = new HashSet<String>(); 21 watch2Paths.put(watcher, paths); 22 23 paths.add(path); 24 25 26 public synchronized void removeWatcher(Watcher watcher) 27 //从watch2Paths和watchTable删除watcher 28 HashSet<String> paths = watch2Paths.remove(watcher); 29 if (paths == null) 30 return; 31 32 for (String p : paths) 33 HashSet<Watcher> list = watchTable.get(p); 34 if (list != null) 35 list.remove(watcher); 36 if (list.size() == 0) 37 watchTable.remove(p); 38 39 40 41 42 //触发watcher 43 public Set<Watcher> triggerWatch(String path, EventType type, Set<Watcher> supress) 44 WatchedEvent e = new WatchedEvent(type, 45 KeeperState.SyncConnected, path); 46 HashSet<Watcher> watchers; 47 synchronized (this) 48 //zookeeper的通知是一次性的,也就是说如果一个路径触发通知后,相应的watcher会从这两个hashmap中删除。 49 watchers = watchTable.remove(path); 50 for (Watcher w : watchers) 51 HashSet<String> paths = watch2Paths.get(w); 52 if (paths != null) 53 paths.remove(path); 54 55 56 57 for (Watcher w : watchers) 58 if (supress != null && supress.contains(w)) 59 continue; 60 61 w.process(e); 62 63 return watchers; 64 65
临时节点
zookeeper中有一类节点在创建的session结束后会被清除掉,zookeeper在创建这些节点时会记录节点和session 的对应关系,到session结束是,删除这些节点。
结束session(DataTree.killSession)
1 //session与零时节点对应关系 2 private final Map<Long, HashSet<String>> ephemerals = 3 new ConcurrentHashMap<Long, HashSet<String>>(); 4 //关闭session 5 void killSession(long session, long zxid) 6 //session结束后,删除零时节点 7 HashSet<String> list = ephemerals.remove(session); 8 if (list != null) 9 for (String path : list) 10 try 11 deleteNode(path, zxid); 12 catch (NoNodeException e) 13 LOG.warn("Ignoring NoNodeException for path " + path 14 + " while removing ephemeral for dead session 0x" 15 + Long.toHexString(session)); 16 17 18 19
权限管理
zookeeper的每个节点都会存储该节点可以访问的用户已经可以执行的操作。
权限(ACL)
1 public class ACL implements Record 2 //perms即权限,有五种权限:READ(可读);WRITE(可写);CREATE(可创建子节点);DELETE(可删除子节点);ADMIN(管理权限);perms的每一位代表一种权限。 3 private int perms; 4 //id是授权的对象。 5 private org.apache.zookeeper.data.Id id; 6 7 public class Id implements Record 8 //scheme是权限模式,有五种模式:digest(通过用户名密码,id为user:password);auth();ip(通过ip,id为ip地址);world(固定用户为anyone,为所有Client端开放权限 );super(对应的id拥有超级权限)。 9 private String scheme; 10 private String id; 11
每个节点可以设置多个权限,实际节点权限只存储一个整数,对应的acl信息保存在两个hashmap中。(DataTree.java)
1 public final Map<Long, List<ACL>> longKeyMap = new HashMap<Long, List<ACL>>(); 2 public final Map<List<ACL>, Long> aclKeyMap = new HashMap<List<ACL>, Long>();
节点操作
zookeeper源码分析之expiryqueue(代码片段)
ExpiryQueue是zookeeper管理客户端连接超时的工具类。它是将松散的时间按expirationInterval间隔映射成一个一个具体的时间点。计算时间所属时间段的算法:privatelongroundToNextInterval(longtime)return(time/expirationInterval+1)*expirationInterval... 查看详情
zookeeper源码分析之expiryqueue(代码片段)
ExpiryQueue是zookeeper管理客户端连接超时的工具类。它是将松散的时间按expirationInterval间隔映射成一个一个具体的时间点。计算时间所属时间段的算法:privatelongroundToNextInterval(longtime)return(time/expirationInterval+1)*expirationInterval... 查看详情
zookeeper源码之服务端启动模块
...启动模块主要负责解析配置文件,启动服务器监听并执行zookeeper命令。类图 QuorumPeerMain QuorumPeerMain是服务端主程序,主要功能是解析配置文件,启动zookeeper服务。内部使用QuorumPeerConfig来解析配置文件;使用QuorumPeer来解析... 查看详情
zookeeper客户端源码——向服务端发起请求(顺序响应+同步阻塞+异步回调)(代码片段)
...输出高质量文章,希望对你有所帮助。本篇源码基于ZooKeeper3.7.0版本。一、向服务端发起请求客户端与服务端通信的最小单元是Packet。所有请求在发送给服务端之前,都需要先构建一个Packet,再将Packet提交给请求处理... 查看详情
eureka源码之服务端接收注册信息(代码片段)
Eureka注册中心系列文章汇总:[领导让我研究Eureka源码|启动过程][领导“叕”让我研究Eureka源码:注册过程[值得收藏的Eureka控制台详解]大家好,我是悟空。本篇从源码角度带你学习Eureka服务端接收注册的流程。另外我从源码中也... 查看详情
zookeeper源码之客户端网络通信模块
ClientCnxn 为客户端发送请求到服务端,管理底层IO连接。将用户调用的请求对象(RequestHeader、Request)封装成Packet对象,存入发送队列。内部有一个线程会不断读取发送队列中的Packet对象,通过NIO将Packet对象发送到服务端,然... 查看详情
zookeeper客户端源码——向服务端建立连接+会话建立+心跳保持长连接(代码片段)
...f0c;用心输出高质量文章,希望对你有所帮助。一、从ZooKeeper实例初始化开始ZooKeeper提供了原生的客户端库,虽然不好用,但是能够更好理解客户端与服务端建立连接和通信的过程。比较流行的ApacheCurator也是对原生库... 查看详情
一文带你了解大数据技术之zookeeper(入门级)(代码片段)
大数据技术之Zookeeper入门1.Zookeeper概述2.Zookeeper特点3.数据结构4.应用场景5.下载地址1.Zookeeper概述Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。Zookeeper的工作机制:2.Zookeeper特点3.数据结构4.应用... 查看详情
原理系列之——zookeeper的watch监控机制(代码片段)
在进入今天的正题之前,先来简单介绍下Zookeeper: Zookeeper是一个分布式应用程序协调服务,保证数据的一致性,其提供的功能包括:配置维护、域名维护、分布式同步、组服务等。 watch监控机制是zookee... 查看详情
zookeeper--初识zookeeperzookeeper的安装和配置zookeeper命令操作(zookeeper数据模型zookeeper服务端/客户端常用命令)(代码片段)
文章目录1.初识Zookeeper2.Zookeeper的安装和配置2.1环境准备2.2上传并解压2.3配置启动2.4启动ZooKeeper2.5查看ZooKeeper状态3.Zookeeper命令操作3.1Zookeeper数据模型3.2Zookeeper服务端常用命令3.3Zookeeper客户端常用命令1.Zookeeper–初识Zookeeper、Zookeep... 查看详情
大数据技术之zookeeper(代码片段)
文章目录1Zookeeper入门1.1概述1.2Zookeeper特点1.3数据结构1.4应用场景2Zookeeper安装2.1本地模式安装部署2.2配置参数解读3Zookeeper实战(开发重点)3.1分布式安装部署3.2客户端命令行操作3.3API应用3.4监听服务器节点动态上下线案... 查看详情
大数据技术之zookeeper(代码片段)
文章目录1Zookeeper入门1.1概述1.2Zookeeper特点1.3数据结构1.4应用场景2Zookeeper安装2.1本地模式安装部署2.2配置参数解读3Zookeeper实战(开发重点)3.1分布式安装部署3.2客户端命令行操作3.3API应用3.4监听服务器节点动态上下线案... 查看详情
源码角度了解skywalking之服务端oap的启动流程(代码片段)
源码角度了解Skywalking之服务端OAP的启动流程Skywalking的服务端OAP的逻辑对应oap-server模块,入口是server-starter子模块的OAPServerStartUp类,直接调用了server-bootstrap子模块的OAPServerBootstrap的start()方法OAPServerBootstrap的start()方法:publicstatic... 查看详情
zookeepersession源码(代码片段)
...的请求全称是网络请求,涉及到网络就少不了Socket通信,ZooKeeper采取的是NIO的方式,提供了一个NIOServerCnxn实例来维护每一个客户端的连接,也就是说客户端与服务端的通信都是靠NIOServerCnxn这个类来处理的,无非就干两件事:接... 查看详情
源码角度了解skywalking之服务端oap对trace的处理(代码片段)
源码角度了解Skywalking之服务端OAP对Trace的处理从前几篇的文章我们知道Skywalking对Trace信息进行生成收集后,将TraceSegment对象转换为UpstreamSegment对象,通过GRPC发送给OAP服务端,服务端处理对应的模块是skywalking-trace-receiver-plugin模块... 查看详情
zookeeper的zab协议的原理以及底层源码实现超级详解(代码片段)
zookeeper的ZAB协议的原理以及实现一,zookeeper的ZAB协议1,ZAB概述二,ZAB协议流程的源码实现1,客户端建立连接2,客户端写数据3,服务端接收数据4,服务端主结点处理数据5,主结点同步数据到从结... 查看详情
zookeeper客户端源码——向服务端建立连接+会话建立+心跳保持长连接(代码片段)
...f0c;用心输出高质量文章,希望对你有所帮助。一、从ZooKeeper实例初始化开始ZooKeeper提供了原生的客户端库,虽然不好用,但是能够更好理解客户端与服务端建立连接和通信的过程。比较流行的ApacheCurator也是对原生库... 查看详情
zookeeper源码阅读server端watcher(代码片段)
前言前面一篇主要介绍了Watcher接口相关的接口和实体类,但是主要是zk客户端相关的代码,如前一篇开头所说,client需要把watcher注册到server端,这一篇分析下server端的watcher。主要分析Watchmanager类。Watchmanager这是WatchManager的类图... 查看详情