【ZooKeeper】服务器启动

本节将向读者介绍 ZooKeeper 服务器的启动过程,下面先从单机版的服务器开始介绍。
【ZooKeeper】服务器启动

1 单机版服务器启动

ZooKeeper 服务器的启动,大体可以分为以下五个主要步骤:配置文件解析、初始化数 据管理器、初始化网络 I/O 管理器、数据恢复和对外服务。图所示是单机版 ZooKeeper 服务器的启动流程图。
【ZooKeeper】服务器启动

预启动

预启动的步骤如下。

  1. 统一由 QuorumPeerMain 作为启动类。

    无论是单机版还是集群模式启动 ZooKeeper 服务器,在 zArServercmc/ 和 zkServer.sh 两个脚本中,都配置了使用 org. apache.zookeeper.server.quorum. QuorumPeerMain 作为启动入口类。

  2. 解析配置文件 zoo.cfg

    ZooKeeper 首先会进行配置文件的解析,配置文件的解析其实就是对 zoo.cfg 文件 的解析。该文件配置了 ZooKeeper 运行时的基本参数,包括 tickTime, dataDir 和 clientport 等参数。

  3. 创建并启动历史文件清理器 DatadirCleanupManager 。

    从 3.4.0 版本开始, ZooKeeper 增加了自动清理历史数据文件的机制,包括对事务 日志和快照数据文件进行定时清理。

  4. 判断当前是集群模式还是单机模式的启动。

    ZooKeeper 根据步骤 2 中解析出的集群服务器地址列表来判断当前是集群模式还 是单机模式,如果是单机模式,那么就委托给 ZooKeeperServerMain 进行启 动处理。

  5. 再次进行配置文件 zoo.cfg 的解析。

  6. 创建服务器实例 ZooKeeperServer 。

    org .apache. zookeeper.server. ZooKeeperServer 是单机版 ZooKeeper 服务端最为核心的实体类。 ZooKeeper 服务器首先会进行服务器实例的创建,接下去的步骤则都是对该服务器实例的初始化工作,包括连接器、内存数据库和请求处理器等组件的初始化。

初始化

初始化的步骤如下。

  1. 创建服务器统计器 ServerStats
    Serverstats 是 ZooKeeper 服务器运行时的统计器,包含了最基本的运行时信 息
    【ZooKeeper】服务器启动

  2. 创建 ZooKeeper 数据管理器 FileTxnSnapLog 。

    FileTxnSnapLog 是 ZooKeeper 上层服务器和底层数据存储之间的对接层,提供 了一系列操作数据文件的接口,包括事务日志文件和快照数据文件。 ZooKeeper 根据 zoo. cfg 中解析出的快照数据目录 dataDir 和事务日志目录 dataLogDir 来创建 FileTxnSnapLog 。

  3. 设置服务器 tickTime 和会话超时时间限制。

  4. 创建 ServerCnxnFactory

    在早期版本中, ZooKeeper 都是自己实现 NIO 框架,从 3.4.0 版本开始,引入了 Netty 读者可以通过配置系统属性 zookeeper.serverCnxnFactory 来指定使用 ZooKeeper 自己实现的 NIO 还是使用 Netty 框架来作为 ZooKeeper 服务端网络连接 工厂。

  5. 初始化 ServerCnxnFactory 。

    ZooKeeper 首先会初始化一个 Thread, 作为整个 ServerCnxnFactory 的主线程, 然后再初始化NIO 服务器。

  6. 启动 ServerCnxnFactory 主线程。

    启动步骤 5 中已经初始化的主线程 ServerCnxnFactory 的主逻辑( run 方法)。需要注意的一点是,虽然这里 ZooKeeper 的 NIO 服务器已经对外开放端口,客户 端能够访问到 ZooKeeper 的客户端服务端口 2181, 但是此时 ZooKeeper 服务器是 无法正常处理客户端请求的。

  7. 恢复本地数据。

    每次在 ZooKeeper 启动的时候,都需要从本地快照数据文件和事务日志文件中进行 数据恢复。

  8. 创建并启动会话管理器。

    在 ZooKeeper 启动阶段,会创建一个会话管理器 SessionTracker 。关于 SessionTracker, 我 们已经进行了讲解,它主要负责 ZooKeeper 服务端的会话管理。创建 SessionTracker 的 时 候 , 会初始化 expirationinterval, nextExpirationTime 和sessionsWithTimeout (用于保存每个会话的超时时间),同时还会计算出一个初 始化的sessionID
    SessionTracker 初始化完毕后, ZooKeeper 就会立即开始会话管理器的会话超时检查。

  9. 初始化 ZooKeeper 的请求处理链。

    ZooKeeper 的请求处理方式是典型的责任链模式的实现,在 ZooKeeper 服务器上, 会有多个请求处理器依次来处理一个客户端请求。在服务器启动的时候,会将这 些请求处理器串 联 起 来 形 成 一 个 请 求 处 理 链 。 单 机 版 服 务 器 的 请 求 处 理 链 主 要 包 括 PrepRequestProcessor、SyncRequestProcessor、 FinalRequestProcessor 三个请求处理器
    【ZooKeeper】服务器启动

  10. 注册 JMX 服务。

    ZooKeeper 会将服务器运行时的一些信息以 JMX 的方式暴露给外部

  11. 注册 ZooKeeper 服务器实例。

    在步骤 6 中, ZooKeeper 已经将 ServerCnxnFactory 主线程启动,但是同时 我们提到此时ZooKeeper 依旧无法处理客户端请求,原因就是此时网络层尚不能 够访问 ZooKeeper 服务器实例。在经过后续步骤的初始化后, ZooKeeper 服务器 实例已经初始化完毕,只需要注册给 ServerCnxnFactory 即可,之后, ZooKeeper 就可以对外提供正常的服务了。

至此,单机版的 ZooKeeper 服务器启动完毕。

2 集群版服务器启动

我们已经讲解了单机版 ZooKeeper 服务器的启动过程,在本节中,我们 将对集群版 ZooKeeper 服务器的启动过程做详细讲解。集群版和单机版 ZooKeeper 服务 器的启动过程在很地方都是一致的,因此本节只会对有差异的地方展开进行讲解。
【ZooKeeper】服务器启动

预启动

预启动的步骤如下。

  1. 统一由 QuorumPeerMain 作为启动类。
  2. 解析配置文件 zoo.cjg。
  3. 创建并启动历史文件清理器 DatadirCleanupManager 。
  4. 判断当前是集群模式还是单机模式的启动。
    在集群模式中,由于已经在 zoo.cfg 中配置了多个服务器地址,因此此处选择集群 模式启动 ZooKeeper 。

初始化

初始化的步骤如下。

  1. 创建 ServerCnxnFactory

  2. 初始化 ServerCnxnFactory 。

  3. 创建 ZooKeeper 数据管理器 FileTxnSnapLog

  4. 创建 QuorumPeer 实例。

    Quorum 是集群模式下特有的对象,是 ZooKeeper 服务器实例 (ZooKeeperServer) 的托管者,从集群层面看, QuorumPeer 代表了 ZooKeeper 集群中的一台机器。在运 行期间,QuorumPeer 会不断检测当前服务器实例的运行状态,同时根据情况发起 Leader 选举。

  5. 创建内存数据库 ZKDatabase 。

    ZKDatabase 是 ZooKeeper 的内存数据库,负责管理 ZooKeeper 的所有会话记录 以及
    DataTree 和事务日志的存储。

  6. 初始化 QuorumPeer 。

    在步骤 5 中我们已经提到, QuorumPeer 是 ZooKeeperServer 的托管者,因 此需要将一些核心组件注册到 QuorumPeer 中去,包括 FileTxnSnapLog, ServerCnxnFactory 和ZKDatabase 。同时 ZooKeeper 还会对 QuorumPeer 配置一些参数,包括服务器地址列表、Leader 选举算法和会话超时时间限制等。

  7. 恢复本地数据。

  8. 启动 ServerCnxnFactory 主线程。

Leader 选举

Leader 选举的步骤如下。

  1. 初始化 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 选举端口的监听,等待集群中其他服务器创建连接。

  2. 注册 JMX 服务。

  3. 检测当前服务器状态。

    在上文中,我们已经提到 QuorumPeer 是 ZooKeeper 服务器实例的托管者,在运 行期间,QuorumPeer 的核心工作就是不断地检测当前服务器的状态,并做出相 应的处理。在正常情况下, ZooKeeper 服务器的状态在 LOOKING, LEADING 和 FOLLOWING/OBSERVING之间进行切换。而在启动阶段, QuorumPeer 的初始 状态是 LOOKING, 因此开始进行Leader 选举。

  4. Leader 选举

    ZooKeeper 的 Leader 选举过程,简单地讲,就是一个集群中所有的机器相互之间 进行一系列投票,选举产生最合适的机器成为 Leader, 同时其余机器成为 Follower 或是 Observer 的集群机器角色初始化过程。关于 Leader 选举算法,简而言之,就 是集群中哪个机器处理的数据越新(通常我们根据每个服务器处理过的最大 ZXID 来比较确定其数据是否更新),其越有可能成为 Leader 。当然,如果集群中的所有 机器处理的 ZXID 一致的话,那么 SID 最大的服务器成为 Leader

Leader 和 Follower 启动期交互过程

到这里为止, ZooKeeper 已经完成了 Leader 选举,并且集群中每个服务器都已经确定了 自己的角色 通常情况下就分为 Leader 和 Follower 两种角色。下面我们来对 Leader 和 Follower 在启动期间的工作原理进行讲解,其大致交互流程如图所示。
【ZooKeeper】服务器启动

Leader 和 Follower 服务器启动期交互过程包括如下步骤。

  1. 创建 Leader 服务器和 Follower 服务器。

    完成 Leader 选举之后,每个服务器都会根据自己的服务器角色创建相应的服务器 实例,并开始进入各自角色的主流程。

  2. Leader 服务器启动 Follower 接收器 LearnerCnxAcceptor 。

    在 ZooKeeper 集群运行期间, Leader 服务器需要和所有其余的服务器(我们 使 用 “Learner” 来 指 代 这 类 机 器 ) 保 持 连 接 以 确 定 集 群 的 机 器 存 活 情 况 。LearnerCnxAcceptor 接收器用于负责接收所有非 Leader 服务器的连接 请求。

  3. Learner 服务器开始和 Leader 建立连接。

    所有的 Learner 服务器在启动完毕后,会从 Leader 选举的投票结果中找到当前集 群中的Leader 服务器,然后与其建立连接。

  4. Leader 服务器创建 LearnerHandler 。

    Leader 接收到来自其他机器的连接创建请求后,会创建一个 LearnerHandler 实例。每个LearnerHandler 实例都对应了一个 Leader 与 Learner 服务器之间 的连接,其负责 Leader 和Learner 服务器之间几乎所有的消息通信和数据同步。

  5. 向 Leader 注册。

    当和 Leader 建立起连接后, Learner 就会开始向 Leader 进行注册 ------ 所谓的注册,其实就是将 Learner 服务器自己的基本信息发送给 Leader 服务器,我们称之为 Learnerinfo,包括当前服务器的 SID 和服务器处理的最新的 ZXID 。

  6. 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 了。

  7. 发送 Leader 状态。

    计算出新的 epoch 之后, Leader 会将该信息以一个 LEADERINF 。消息的形式发送 给 Learner, 同时等待 Learner 的响应。

  8. Learner 发送 ACK 消息。

    Follower 在收到来自 Leader 的 LEADERINFO 消息后,会解析出 epoch 和 ZXID, 然后向Leader 反馈一个 ACKEP0CH 响应。

  9. 数据同步。

    Leader 服务器接收到 Learner 的这个 ACK 消息后,就可以开始与其进行数据同步 了。

  10. 启动 Leader 和 Learner 服务器。

    当有过半的 Learner 已经完成了数据同步,那么 Leader 和 Learner 服务器实例就可 以开始启动了。

Leader 和 Follower 启动

Leader 和 Follower 启动的步骤如下。

  1. 创建并启动会话管理器。
  2. 初始化 ZooKeeper 的请求处理链。
    和单机版服务器一样,集群模式下,每个服务器都会在启动阶段串联请求处理链, 只是根据服务器角色不同,会有不同的请求处理链路
  3. 注册 JMX 服务。

至此,集群版的 ZooKeeper 服务器启动完毕。

上一篇:3.6 多任务学习方法


下一篇:xgboost 源码学习