栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

zookeeper源码解析(一)

zookeeper源码解析(一)

从ZKDatabase说起

ZKDatabase类维护着zookeeper的内存数据库,具体包括了数据树,会话信息和事务提交日志等。

首先,我们不妨来看一下ZKDatabase类的静态变量和成员变量。

    //为该类创建日志记录器
    private static final Logger LOG = LoggerFactory.getLogger(ZKDatabase.class);
    //要维护的四个成员变量
    protected DataTree dataTree;
    protected ConcurrentHashMap sessionsWithTimeouts;
    protected FileTxnSnapLog snapLog;
    protected long minCommittedLog, maxCommittedLog;

dataTree:维护了树形数据结构,不包含任何网络相关或客户端连接相关的代码。

sessionWithTimeouts:存储sessionID和对应的过期时间,使用并发哈希表ConcurrentHashMap作为变量类型,这是HashMap的一个线程安全的、支持高效并发的版本。

(PS:这里简单介绍一下sessionID和对应的过期时间的含义。

sessionID: 会话ID,用来唯一标识一个会话,每次客户端创建会话的时候,zookeeper 都会为其分配一个全局唯一的 sessionID。

Timeout:会话超时时间。客户端在构造 Zookeeper 实例时候,向服务端发送配置的超时时间,server 端会根据自己的超时时间限制最终确认会话的超时时间。)

snapLog:维护事务日志和数据快照,snapLog和具体的zookeeper数据库是一一对应的。

minCommittedLog,maxCommittedLog:zookeeper维护commitedLog队列,minCommittedLog和maxCommitedLog分别对应队列中事务编号的最小值和最大值(值得注意的是,zookeeper指定了这个队列有个默认最大长度500如下图,当队列更新要超过最大长度时,会移除目前队列中最早的事务)。

    public static final String COMMIT_LOG_COUNT = "zookeeper.commitLogCount";
    public static final int DEFAULT_COMMIT_LOG_COUNT = 500;
    public int commitLogCount;

PS:zookeeper的事务ID(zxid)遵循如下规则:

zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。

DataTree-zookeeper数据库的核心

上面简单介绍了ZKDatabase类后,让我们先把焦点转到DataTree类。(我们先对ZKDatabase类中一个实例所要维护的这几个变量的结构做深入了解,之后再回到ZKDatabase类,看它提供了哪些函数哪些行为来维护这些变量)。DataTree无疑是zookeeper所构建的内存数据库的核心部分,其维护了一个树形数据结构。

DataTree主要维护两个结构,一个是各个绝对路径到数据节点的哈希表,另一个是由数据节点构成的树。并且对路径的所有访问是通过哈希表,仅在序列化到磁盘时遍历该树。如下成员变量nodes即存储着哈希表,是对哈希表的简单封装。

    //日志
    private static final Logger LOG = LoggerFactory.getLogger(DataTree.class);
    private final RateLogger RATE_LOGGER = new RateLogger(LOG, 15 * 60 * 1000);

    
    private final NodeHashMap nodes;
   
    //watch的管理(zookeeper通过watch对客户端提供了通知节点变化的机制)
    private IWatchManager dataWatches;
    private IWatchManager childWatches;

    //存储所有节点的路径及数据占的空间大小
    private final AtomicLong nodeDataSize = new AtomicLong(0);

    //zookeeper中树的根节点为"/"
    private static final String rootZookeeper = "/";
DataTree构造函数
    public DataTree() {
        this(new DigestCalculator());
    }

    DataTree(DigestCalculator digestCalculator) {
        this.digestCalculator = digestCalculator;
        nodes = new NodeHashMapImpl(digestCalculator);
        
        //添加根节点
        nodes.put("", root);
        nodes.putWithoutDigest(rootZookeeper, root);

        //分别添加配额节点和管理配额的节点
        root.addChild(procChildZookeeper);
        nodes.put(procZookeeper, procDataNode);

        procDataNode.addChild(quotaChildZookeeper);
        nodes.put(quotaZookeeper, quotaDataNode);

        addConfigNode();

        nodeDataSize.set(approximateDataSize());
        try {
            //配置监听器
            dataWatches = WatchManagerFactory.createWatchManager();
            childWatches = WatchManagerFactory.createWatchManager();
        } catch (Exception e) {
            LOG.error("Unexpected exception when creating WatchManager, exiting abnormally", e);
            ServiceUtils.requestSystemExit(ExitCode.UNEXPECTED_ERROR.getValue());
        }
    }

PS:关于配额,是指节点可以设置配额,如限制节点可以有几个子节点。但事实上,如果超过了配额限制,也只是在日志上会出现警告信息,实际内容还是被存储了。

这里把构造函数写成一个public,一个default,而public实际上基本只是调用default构造函数的形式我觉得可能仅仅是为了方便测试,事实上,在系统实现代码上都是调的public的构造函数,只有在测试代码中用到了default构造函数。

题外趣事 

在阅读源码的过程中,我发现即使是出自apache这样代码质量比较高的组织,代码中也会出现p,pp...这样的命名方式。

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/285944.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号