zookeeper源码之服务端数据管理(代码片段)

zwh1988 zwh1988     2022-10-20     742

关键词:

  这一节我们主要来看一下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 
View Code

数节点状态(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 
View Code

配额管理

  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 
View Code

监听器管理

  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 
View Code

临时节点

  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     
View Code

权限管理

  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 
View Code

每个节点可以设置多个权限,实际节点权限只存储一个整数,对应的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>();
View Code

节点操作

View Code

 

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的类图... 查看详情