本节将向读者介绍 ZooKeeper 服务器的启动过程,下面先从单机版的服务器开始介绍。
1 单机版服务器启动
ZooKeeper 服务器的启动,大体可以分为以下五个主要步骤:配置文件解析、初始化数 据管理器、初始化网络 I/O 管理器、数据恢复和对外服务。图所示是单机版 ZooKeeper 服务器的启动流程图。
预启动
预启动的步骤如下。
-
统一由 QuorumPeerMain 作为启动类。
无论是单机版还是集群模式启动 ZooKeeper 服务器,在 zArServercmc/ 和 zkServer.sh 两个脚本中,都配置了使用 org. apache.zookeeper.server.quorum. QuorumPeerMain 作为启动入口类。
-
解析配置文件 zoo.cfg
ZooKeeper 首先会进行配置文件的解析,配置文件的解析其实就是对 zoo.cfg 文件 的解析。该文件配置了 ZooKeeper 运行时的基本参数,包括 tickTime, dataDir 和 clientport 等参数。
-
创建并启动历史文件清理器 DatadirCleanupManager 。
从 3.4.0 版本开始, ZooKeeper 增加了自动清理历史数据文件的机制,包括对事务 日志和快照数据文件进行定时清理。
-
判断当前是集群模式还是单机模式的启动。
ZooKeeper 根据步骤 2 中解析出的集群服务器地址列表来判断当前是集群模式还 是单机模式,如果是单机模式,那么就委托给 ZooKeeperServerMain 进行启 动处理。
-
再次进行配置文件 zoo.cfg 的解析。
-
创建服务器实例 ZooKeeperServer 。
org .apache. zookeeper.server. ZooKeeperServer 是单机版 ZooKeeper 服务端最为核心的实体类。 ZooKeeper 服务器首先会进行服务器实例的创建,接下去的步骤则都是对该服务器实例的初始化工作,包括连接器、内存数据库和请求处理器等组件的初始化。
初始化
初始化的步骤如下。
-
创建服务器统计器 ServerStats
Serverstats 是 ZooKeeper 服务器运行时的统计器,包含了最基本的运行时信 息 -
创建 ZooKeeper 数据管理器 FileTxnSnapLog 。
FileTxnSnapLog 是 ZooKeeper 上层服务器和底层数据存储之间的对接层,提供 了一系列操作数据文件的接口,包括事务日志文件和快照数据文件。 ZooKeeper 根据 zoo. cfg 中解析出的快照数据目录 dataDir 和事务日志目录 dataLogDir 来创建 FileTxnSnapLog 。
-
设置服务器 tickTime 和会话超时时间限制。
-
创建 ServerCnxnFactory
在早期版本中, ZooKeeper 都是自己实现 NIO 框架,从 3.4.0 版本开始,引入了 Netty 读者可以通过配置系统属性 zookeeper.serverCnxnFactory 来指定使用 ZooKeeper 自己实现的 NIO 还是使用 Netty 框架来作为 ZooKeeper 服务端网络连接 工厂。
-
初始化 ServerCnxnFactory 。
ZooKeeper 首先会初始化一个 Thread, 作为整个 ServerCnxnFactory 的主线程, 然后再初始化NIO 服务器。
-
启动 ServerCnxnFactory 主线程。
启动步骤 5 中已经初始化的主线程 ServerCnxnFactory 的主逻辑( run 方法)。需要注意的一点是,虽然这里 ZooKeeper 的 NIO 服务器已经对外开放端口,客户 端能够访问到 ZooKeeper 的客户端服务端口 2181, 但是此时 ZooKeeper 服务器是 无法正常处理客户端请求的。
-
恢复本地数据。
每次在 ZooKeeper 启动的时候,都需要从本地快照数据文件和事务日志文件中进行 数据恢复。
-
创建并启动会话管理器。
在 ZooKeeper 启动阶段,会创建一个会话管理器 SessionTracker 。关于 SessionTracker, 我 们已经进行了讲解,它主要负责 ZooKeeper 服务端的会话管理。创建 SessionTracker 的 时 候 , 会初始化 expirationinterval, nextExpirationTime 和sessionsWithTimeout (用于保存每个会话的超时时间),同时还会计算出一个初 始化的sessionID
SessionTracker 初始化完毕后, ZooKeeper 就会立即开始会话管理器的会话超时检查。 -
初始化 ZooKeeper 的请求处理链。
ZooKeeper 的请求处理方式是典型的责任链模式的实现,在 ZooKeeper 服务器上, 会有多个请求处理器依次来处理一个客户端请求。在服务器启动的时候,会将这 些请求处理器串 联 起 来 形 成 一 个 请 求 处 理 链 。 单 机 版 服 务 器 的 请 求 处 理 链 主 要 包 括 PrepRequestProcessor、SyncRequestProcessor、 FinalRequestProcessor 三个请求处理器
-
注册 JMX 服务。
ZooKeeper 会将服务器运行时的一些信息以 JMX 的方式暴露给外部
-
注册 ZooKeeper 服务器实例。
在步骤 6 中, ZooKeeper 已经将 ServerCnxnFactory 主线程启动,但是同时 我们提到此时ZooKeeper 依旧无法处理客户端请求,原因就是此时网络层尚不能 够访问 ZooKeeper 服务器实例。在经过后续步骤的初始化后, ZooKeeper 服务器 实例已经初始化完毕,只需要注册给 ServerCnxnFactory 即可,之后, ZooKeeper 就可以对外提供正常的服务了。
至此,单机版的 ZooKeeper 服务器启动完毕。
2 集群版服务器启动
我们已经讲解了单机版 ZooKeeper 服务器的启动过程,在本节中,我们 将对集群版 ZooKeeper 服务器的启动过程做详细讲解。集群版和单机版 ZooKeeper 服务 器的启动过程在很地方都是一致的,因此本节只会对有差异的地方展开进行讲解。
预启动
预启动的步骤如下。
- 统一由 QuorumPeerMain 作为启动类。
- 解析配置文件 zoo.cjg。
- 创建并启动历史文件清理器 DatadirCleanupManager 。
- 判断当前是集群模式还是单机模式的启动。
在集群模式中,由于已经在 zoo.cfg 中配置了多个服务器地址,因此此处选择集群 模式启动 ZooKeeper 。
初始化
初始化的步骤如下。
-
创建 ServerCnxnFactory
-
初始化 ServerCnxnFactory 。
-
创建 ZooKeeper 数据管理器 FileTxnSnapLog
-
创建 QuorumPeer 实例。
Quorum 是集群模式下特有的对象,是 ZooKeeper 服务器实例 (ZooKeeperServer) 的托管者,从集群层面看, QuorumPeer 代表了 ZooKeeper 集群中的一台机器。在运 行期间,QuorumPeer 会不断检测当前服务器实例的运行状态,同时根据情况发起 Leader 选举。
-
创建内存数据库 ZKDatabase 。
ZKDatabase 是 ZooKeeper 的内存数据库,负责管理 ZooKeeper 的所有会话记录 以及
DataTree 和事务日志的存储。 -
初始化 QuorumPeer 。
在步骤 5 中我们已经提到, QuorumPeer 是 ZooKeeperServer 的托管者,因 此需要将一些核心组件注册到 QuorumPeer 中去,包括 FileTxnSnapLog, ServerCnxnFactory 和ZKDatabase 。同时 ZooKeeper 还会对 QuorumPeer 配置一些参数,包括服务器地址列表、Leader 选举算法和会话超时时间限制等。
-
恢复本地数据。
-
启动 ServerCnxnFactory 主线程。
Leader 选举
Leader 选举的步骤如下。
-
初始化 Leader 选举。
Leader 选举可以说是集群和单机模式启动 ZooKeeper 最大的不同点。 ZooKeeper 首先会根据自身的 SID (服务器 ID) 、 lastLoggedZxid (最新的 ZXID) 和当 前的服务器 epoch(currentEpoch) 来生成一个初始化的投票一简单地讲, 在初始化过程中每个服务器都会给自己投票。
然后, ZooKeeper 会根据 zoo.cfg 中的配置,创建相应的 Leader 选举算法实现。在ZooKeeper 中,默认提供了三种 Leader 选举算法的实现,分别是 LeaderElection 、AuthFastLeaderElection 和 FastLeaderElection, 可以通过在配置文件 (zoo.cfg) 中 使用electionAlg 属性来指定,分别使用数字 0〜3 来表示。从 3.4.0 版本开始, ZooKeeper 废弃了 前两种 Leader 选举算法,只支持 FastLeaderElection 选举算法了。 在初始化阶段, ZooKeeper 会首先创建 Leader 选举所需 的网络 I/O 层 QuorumCnxManager, 同时启动对 Leader 选举端口的监听,等待集群中其他服务器创建连接。
-
注册 JMX 服务。
-
检测当前服务器状态。
在上文中,我们已经提到 QuorumPeer 是 ZooKeeper 服务器实例的托管者,在运 行期间,QuorumPeer 的核心工作就是不断地检测当前服务器的状态,并做出相 应的处理。在正常情况下, ZooKeeper 服务器的状态在 LOOKING, LEADING 和 FOLLOWING/OBSERVING之间进行切换。而在启动阶段, QuorumPeer 的初始 状态是 LOOKING, 因此开始进行Leader 选举。
-
Leader 选举
ZooKeeper 的 Leader 选举过程,简单地讲,就是一个集群中所有的机器相互之间 进行一系列投票,选举产生最合适的机器成为 Leader, 同时其余机器成为 Follower 或是 Observer 的集群机器角色初始化过程。关于 Leader 选举算法,简而言之,就 是集群中哪个机器处理的数据越新(通常我们根据每个服务器处理过的最大 ZXID 来比较确定其数据是否更新),其越有可能成为 Leader 。当然,如果集群中的所有 机器处理的 ZXID 一致的话,那么 SID 最大的服务器成为 Leader
Leader 和 Follower 启动期交互过程
到这里为止, ZooKeeper 已经完成了 Leader 选举,并且集群中每个服务器都已经确定了 自己的角色 通常情况下就分为 Leader 和 Follower 两种角色。下面我们来对 Leader 和 Follower 在启动期间的工作原理进行讲解,其大致交互流程如图所示。
Leader 和 Follower 服务器启动期交互过程包括如下步骤。
-
创建 Leader 服务器和 Follower 服务器。
完成 Leader 选举之后,每个服务器都会根据自己的服务器角色创建相应的服务器 实例,并开始进入各自角色的主流程。
-
Leader 服务器启动 Follower 接收器 LearnerCnxAcceptor 。
在 ZooKeeper 集群运行期间, Leader 服务器需要和所有其余的服务器(我们 使 用 “Learner” 来 指 代 这 类 机 器 ) 保 持 连 接 以 确 定 集 群 的 机 器 存 活 情 况 。LearnerCnxAcceptor 接收器用于负责接收所有非 Leader 服务器的连接 请求。
-
Learner 服务器开始和 Leader 建立连接。
所有的 Learner 服务器在启动完毕后,会从 Leader 选举的投票结果中找到当前集 群中的Leader 服务器,然后与其建立连接。
-
Leader 服务器创建 LearnerHandler 。
Leader 接收到来自其他机器的连接创建请求后,会创建一个 LearnerHandler 实例。每个LearnerHandler 实例都对应了一个 Leader 与 Learner 服务器之间 的连接,其负责 Leader 和Learner 服务器之间几乎所有的消息通信和数据同步。
-
向 Leader 注册。
当和 Leader 建立起连接后, Learner 就会开始向 Leader 进行注册 ------ 所谓的注册,其实就是将 Learner 服务器自己的基本信息发送给 Leader 服务器,我们称之为 Learnerinfo,包括当前服务器的 SID 和服务器处理的最新的 ZXID 。
-
Leader 解析 Learner 信息,计算新的 epoch
Leader 服务器在接收到 Learner 的基本信息后,会解析出该 Learner 的 SID 和 ZXID, 然后根据该 Learner 的 ZXID 解析出其对应的 epoch_of_learner, 和当前 Leader 服 务器的 epoch_of_leader 进行比较,如果该 Learner 的 epoch_of_learner 更大的话, 那么就更新 Leader 的epoch:
epoch_of_leader = epochoflearner + 1
。然后, LearnerHandler 会进行等待,直到过半的 Learner 已经向 Leader 进行了 注册,同时更新了 epoch_of_leader 之后, Leader 就可以确定当前集群的 epoch 了。 -
发送 Leader 状态。
计算出新的 epoch 之后, Leader 会将该信息以一个 LEADERINF 。消息的形式发送 给 Learner, 同时等待 Learner 的响应。
-
Learner 发送 ACK 消息。
Follower 在收到来自 Leader 的 LEADERINFO 消息后,会解析出 epoch 和 ZXID, 然后向Leader 反馈一个 ACKEP0CH 响应。
-
数据同步。
Leader 服务器接收到 Learner 的这个 ACK 消息后,就可以开始与其进行数据同步 了。
-
启动 Leader 和 Learner 服务器。
当有过半的 Learner 已经完成了数据同步,那么 Leader 和 Learner 服务器实例就可 以开始启动了。
Leader 和 Follower 启动
Leader 和 Follower 启动的步骤如下。
- 创建并启动会话管理器。
- 初始化 ZooKeeper 的请求处理链。
和单机版服务器一样,集群模式下,每个服务器都会在启动阶段串联请求处理链, 只是根据服务器角色不同,会有不同的请求处理链路 - 注册 JMX 服务。
至此,集群版的 ZooKeeper 服务器启动完毕。