在ZooKeeper服务器启动期间,首先会进行数据初始化工作,用于将存储在磁盘上的数据文件加载到ZooKeeper服务器内存中。
初始化流程
数据初始化工作其实就是从磁盘中加载数据的过程,主要包括了从快照文件中加载快照数据的根据事务日志进行数据订正两个过程。
1.初始化FileTxnSnapLog
FileTxnSnapLog是ZooKeeper事务日志和快照数据访问层,用于衔接上层业务与底层数据存储。底层数据包括了事务日志和快照两部分,因此FileTxnSnapLog内部氛围FileTxnLog和FileSnap的初始化,分别代表事务日志管理器和快照数据管理器的初始化。
2.初始化ZKDatabase
接下来就开始构建内存数据库ZKDatabase了。在初始化过程中,首先会构建一个初始化的DataTree,同时将步骤1中初始化的FileTxnSnapLog交给ZKDatabase,以便于内存数据库能够对事务日志和快照数据进行访问。
DataTree是ZooKeeper内存模型的核心模型,简而言之就是一棵树,保存了ZooKeeper上的所有节点信息,在每个ZooKeeper服务器内部都是单例。在ZKDatabase初始化的时候,DataTree也会进行相应的初始化工作——创建一些ZooKeeper的默认节点,包括/、/zookeeper、/zookeeper/quota三个节点的创建。
除了ZooKeeper的数据节点,在ZKDatabase的初始化阶段还会创建一个用于保存所有客户端会话超时时间的记录器:sessionsWithTimeouts——会话超时时间记录器。
3.创建PlayBackListener监听器
PlayBackListener监听器主要用来接收事务应用过程中的回调。在后面读者会看到,在ZooKeeper数据恢复后期,会有一个事务订正过程,在这个过程中会回调PlayBackListener监听器来进行对应的数据订正。
4.处理快照文件
完成内存数据库的初始化之后,ZooKeeper就开始从磁盘中恢复数据了。在上文中我们已经提到,每一个快照数据文件中都保存了ZooKeeper服务器近似全量的数据,因此首先从这些快照文件开始加载。
5.获取最新的100个快照文件
ZooKeeper服务器运行一段时间之后,磁盘上会保留许多快照文件。另外由于每次数据快照过程中,ZooKeeper都会将全量数据Dump到磁盘快照文件中,因此往往更新时间最晚的那个文件包含了最新的全量数据。那么是否我们只需要这个罪行的快照文件就可以了呢?在ZooKeeper的实现中,会获取最新的之多100个快照文件。
6.解析快照文件
获取到这之多100个文件之后,ZooKeeper会“逐个”进行解析每个快照文件都是内存数据序列化到磁盘的二进制文件,因此在这里需要对其进行反序列化,生成DataTree对象和sessionsWithTimeouts集合。同时在这个过程中,还会进行文件的checkSum校验以确认快照文件的正确性。
在“逐个”解析的过程中,如果正确性校验通过的话,呢么通常只会解析最新的那个快照文件。换句话说,只有当最新的快照文件不可用的时候,才会逐个进行解析,知道将这100个文件全部解析完成。如果将步骤4中获取的所有快照文件都解析完成后还是无法完成恢复一个完整的DataTree和sessionWithTimeouts,则认为无法从磁盘中加载数据,服务器启动失败。
7.获取罪行的ZXID
完成6之后,就已经基于开招文件构建了一个完整的DataTree实例和sessionsWithTimeouts集合了。此时根据这个快照文件的文件名就可以解析出一个最新的ZXID:zxid_for_snap,它代表了ZooKeeper开始进行数据快照的时刻。
8.处理事务日志
在经过前面7处理后,此时ZooKeeper服务器内存中已经有了一份近似全量的数据了,开始就要通过事务日志来更新增量数据了。
9.获取所有zxid_for_snap之后提交的事务
到这里,我们已经获取到了快照数据的最新ZXID。ZooKeeper中数据的快照机制决定了快照文件中并非包含了所有的事务操作。蛋是未被包含在快照中的那部分事务操作是可以我替你故宫 数据订正来实现的。因此这里我们只需要从事务日志中获取所有ZXID比步骤7中得到的zxid_for_snap大的事务操作。
10.事务应用
获取到所有ZXID大于zxid_for_snap的事务后,将其逐个应用到之前基于快照数据文件恢复出来的DataTree和sessionsWithTimeouts中去。在事务应用的过程中,还有一个细节需要我们注意,每当有一个事务被应用到内存数据库中,ZooKeeper同时会回调PlayBackListener监听器,将这一事务操作记录转换成Proposal,保存到ZKDatabase.committedLog中,以便Follower进行快速同步。
11.获取最新ZXID
待所有事务都被完整地应用到内存数据库中,基本上就完成了数据的初始化过程,此时再次获取一个ZXID,用来标识上次服务器正常运行时提交的最大事务ID。
12.校验epoch
epoch是ZooKeeper中一个非常特别的变量,其字面意思是“时代”,在ZooKeeper中,epoch标识了当前Leader周期。每次选举产生一个新的Leader服务器之后,就会生成一个新的cpoch。在运行期间集群中机器互相通信的过程中,都会带上这个epoch一确保彼此在同一个Leader周期内。
在完成数据加载后,ZooKeeper会从步骤11中确定的ZXID中解析出事务处理的Leader周期:epochOfZxid。同时会从磁盘的currentEpoch和acceptedEpoch文件中对去出上次记录的最新的epoch值,进行校验。
以上就是ZooKeeper服务器启动时期的数据初始化的全过程。
更多精彩内容,欢迎关注微信公众号:Java小笔记(ijavanote)