zookeeper服务器初始化的过程

来源:互联网 发布:数据分析师证书难不难 编辑:程序博客网 时间:2024/05/01 09:57

  在Zookeeper服务器启动期间,首先会进行数据初始化工作,用于将存储在磁盘上的数据文件加载到Zookeeper服务器内存中。

初始化流程

整体流程图如下:
这里写图片描述
数据的初始化工作是从磁盘上加载数据的过程,主要包括了从快照文件中加载快照数据和根据事务日志进行数据修正两个过程

1.初始化FileTxnSnapLog。FileTxnSnapLog是Zookeeper事务日志和快照数据访问层,用于衔接上层业务和底层数据存储,底层数据包含了事务日志和快照数据两部分。FileTxnSnapLog中对应FileTxnLog和FileSnap。

2.初始化ZKDatabase。首先构建DataTree,同时将FileTxnSnapLog交付ZKDatabase,以便内存数据库能够对事务日志和快照数据进行访问。
除了ZooKeeper的数据节点,在ZKDatabase的初始化阶段还会创建一个用于保存所有客户端会话超时时间的记录器:sessionsWithTimeouts——会话超时时间记录器。

QuorumPeer.java    private void loadDataBase() {        try {            zkDb.loadDataBase();            // load the epochs            long lastProcessedZxid = zkDb.getDataTree().lastProcessedZxid;            long epochOfZxid = ZxidUtils.getEpochFromZxid(lastProcessedZxid);            try {                currentEpoch = readLongFromFile(CURRENT_EPOCH_FILENAME);                if (epochOfZxid > currentEpoch && updating.exists()) {                    setCurrentEpoch(epochOfZxid);                }            }

上面的关键在于zkDb.loadDataBase();

3.创建PlayBackListener监听器
PlayBackListener监听器主要用来接收事务应用过程中的回调。在后面读者会看到,在ZooKeeper数据恢复后期,会有一个事务订正过程,在这个过程中会回调PlayBackListener监听器来进行对应的数据订正。

    public long loadDataBase() throws IOException {        PlayBackListener listener=new PlayBackListener(){            public void onTxnLoaded(TxnHeader hdr,Record txn){                Request r = new Request(null, 0, hdr.getCxid(),hdr.getType(),                        null, null);                r.txn = txn;                r.hdr = hdr;                r.zxid = hdr.getZxid();                addCommittedProposal(r);            }        };        long zxid = snapLog.restore(dataTree,sessionsWithTimeouts,listener);        initialized = true;        return zxid;    }

4.处理快照文件
完成内存数据库的初始化之后,ZooKeeper就开始从磁盘中恢复数据了。在上文中我们已经提到,每一个快照数据文件中都保存了ZooKeeper服务器近似全量的数据,因此首先从这些快照文件开始加载。

5.获取最新的100个快照文件
ZooKeeper服务器运行一段时间之后,磁盘上会保留许多快照文件。另外由于每次数据快照过程中,ZooKeeper都会将全量数据Dump到磁盘快照文件中,因此往往更新时间最晚的那个文件包含了最新的全量数据。那么是否我们只需要这个最新的快照文件就可以了呢?在ZooKeeper的实现中,会获取最新的至多100个快照文件。

6.解析快照文件。逐个解析快照文件,此时需要进行反序列化,生成DataTree和sessionsWithTimeouts,同时还会校验Checksum及快照文件的正确性。对于100个快找文件,如果正确性校验通过时,通常只会解析最新的那个快照文件。只有最新快照文件不可用时,才会逐个进行解析,直至100个快照文件全部解析完。

7.获取最新的ZXID。此时根据快照文件的文件名即可解析出最新的ZXID:zxid_for_snap。该ZXID代表了Zookeeper开始进行数据快照的时刻。

8.处理事务日志。此时服务器内存中已经有了一份近似全量的数据,现在开始通过事务日志来更新增量数据。

9.获取所有zxid_for_snap之后提交的事务。此时,已经可以获取快照数据的最新ZXID。只需要从事务日志中获取所有ZXID比步骤7得到的ZXID大的事务操作。

10.事务应用。获取大于zxid_for_snap的事务后,将其逐个应用到之前基于快照数据文件恢复出来的DataTree和sessionsWithTimeouts。每当有一个事务被应用到内存数据库中后,Zookeeper同时会回调PlayBackListener,将这事务操作记录转换成Proposal,并保存到ZKDatabase的committedLog中,以便Follower进行快速同步。

11.获取最新的ZXID。待所有的事务都被完整地应用到内存数据库中后,也就基本上完成了数据的初始化过程,此时再次获取ZXID,用来标识上次服务器正常运行时提交的最大事务ID。

    public long restore(DataTree dt, Map<Long, Integer> sessions,             PlayBackListener listener) throws IOException {        //5,6,7都是在这一步里完成的        snapLog.deserialize(dt, sessions);         //9.        FileTxnLog txnLog = new FileTxnLog(dataDir);        //获得快照后的第一条日志记录        TxnIterator itr = txnLog.read(dt.lastProcessedZxid+1);        long highestZxid = dt.lastProcessedZxid;        TxnHeader hdr;        try {            while (true) {                hdr = itr.getHeader();                if (hdr == null) {              //没有日志了,说明初始化完成,此时dt.lastProcessedZxid为上一次最后一条日志记录                    return dt.lastProcessedZxid;                }                //更新highestZxid                    highestZxid = hdr.getZxid();                //将日志重新应用内存到数据库中去,此时dt.lastProcessedZxid会变为日志的zxid,代表最后提交的事务id                processTransaction(hdr,dt,sessions, itr.getTxn());                //回调PlayBackListener,将这事务操作记录转换成Proposal,并保存到ZKDatabase的committedLog中                listener.onTxnLoaded(hdr, itr.getTxn());                if (!itr.next())                     break;            }        } finally {            if (itr != null) {                itr.close();            }        }        return highestZxid;    }

12.校验epoch。epoch标识了当前Leader周期,集群机器相互通信时,会带上这个epoch以确保彼此在同一个Leader周期中。完成数据加载后,Zookeeper会从步骤11中确定ZXID中解析出事务处理的Leader周期:epochOfZxid。同时也会从磁盘的currentEpoch和acceptedEpoch文件中读取上次记录的最新的epoch值,进行校验。
currentEpoch和acceptedEpoch都是会持久化的。下一篇我们将我们看看什么时候持久化它们的。

原创粉丝点击