一、客户端

客户端是开发人员使用 ZooKeeper 最主要的途径,因此我们有必要对ZooKeeper 客户端的内部原理进行详细讲解。ZooKeeper 的客户端主要由以下几个核心组件组成。

(1)ZooKeeper 实例∶客户端的入口。

(2)ClientWatchManager∶客户端 Watcher 管理器。

(3) HostProvider∶ 客户端地址列表管理器。

(4)CLientCnxn∶ 客户端核心线程,其内部又包含两个线程,即 SendThread 和 EventThread。前者是一个 I/O 线程,主要负责 ZooKeeper 客户端和服务端之间的网络 I/O 通信;后者是一个事件线程,主要负责对服务端事件进行处理。

二、客户端会话的创建过程

初始化阶段

1.初始化 ZooKeeper 对象。 通过调用ZooKeeper的构造方法来实例化一个ZooKeeper对象,在初始化过程中,会创建一个客户端的 Watcher 管理器∶ CLientwatchManager。

2.设置会话默认 Watcher。 如果在构造方法中传入了一个 Watcher 对象,那么客户端会将这个对象作为默认 Watcher 保存在 ClientwatchManager 中。

3.构造 ZooKeeper 服务器地址列表管理器∶ HostProvider。 对于构造方法中传入的服务器地址,客户端会将其存放在服务器地址列表管理器 HostP rovider 中。

4. 创建并初始化客户端网络连接器∶ CLientCnxn。 ZooKeeper 客户端首先会创建一个网络连接器 CLientCnxn,用来管理客户端与服务器的网络交互。另外,客户端在创建 CLientCnxn的同时,还会初始化客户端两个核心队列 outgoingQueue 和 pendingQueue,分别作为客户端的请求发送队列和服务端响应的等待队列。ClientCnxn 连接器的底层 I/O 处理器是 ClientCnxnSocket,因此在这一步中,客户端还会同时创建 CLientCnxnSocket处理器。

5..初始化 SendThread 和 EventThread。客户端会创建两个核心网络线程 SendThread 和 EventThread,前者用于管理客户端和服务端之间的所有网络 I/O,后者则用于进行客户端的事件处理。同时,客户端还会将 ClientCnxnSocket 分配给 SendThread 作为底层网络 I/O处理器,并初始化 EventThread 的待处理事件队列 waitingEvents,用于存放所有等待被客户端处理的事件。会话创建阶段

会话创建阶段

1.启动 SendThread 和 EventThread SendThread 首先会判断当前客户端的状态,进行一系列清理性工作,为客户端发送"会话创建"请求做准备。

2.获取一个服务器地址。 在开始创建 TCP连接之前,SendThread 首先需要获取一个 ZooKeeper 服务器的目标地址,这通常是从 HostProvider 中随机获取出一个地址,然后委托给 CLientCnxnSocket 去创建与ZooKeeper 服务器之间的 TCP连接。

3. 创建 TCP 连接。 获取到一个服务器地址后,CLientCnxnSocket 负责和服务器创建一个TCP长连接。

4. 构造 ConnectRequest 请求。 在 TCP连接创建完毕后,可能有的读者会认为,这样是否就说明已经和ZooKeeper服务器完成连接了呢?其实不然,步骤 8只是纯粹地从网络 TCP层面完成了客户端与服务端之间的 Socket 连接,但远未完成 ZooKeeper客户端的会话创建。 SendThread 会负责根据当前客户端的实际设置,构造出一个ConnectRequest请求,该请求代表了客户端试图与服务器创建一个会话。同时,ZooKeeper 客户端还会进一步将该请求包装成网络 I/O 层的 Packet 对象,放入请求发送队列 outgoingQueue 中去。

5.发送请求 当客户端请求准备完毕后,就可以开始向服务端发送请求了。CLientCnxnSocket 负责从outgoingQueue 中取出一个待发送的Packet对象,将其序列化成ByteBuffer后,向服务端进行发送。

响应处理阶段

1.接收服务端响应。 CLientCnxnSocket 接收到服务端的响应后,会首先判断当前的客户端状态是否是"已初始化",如果尚未完成初始化,那么就认为该响应一定是会话创建请求的响应,直接交由 readConnectResult 方法来处理该响应。

2.处理 Response。 CLientCnxnSocket 会对接收到的服务端响应进行反序列化,得到 Connect Response 对象,并从中获取到 ZooKeeper 服务端分配的会话 sessionId。

3.连接成功。 连接成功后,一方面需要通知 SendThread 线程,进一步对客户端进行会话参数的设置,包括 readTimeout 和 connectTimeout 等,并更新客户端状态;另一方面,需要通知地址管理器 HostProvider 当前成功连接的服务器地址。

4.生成事件∶SyncConnected-None。 为了能够让上层应用感知到会话的成功创建,SendThread 会生成一个事件 SyncConnected-None,代表客户端与服务器会话创建成功,并将该事件传递给 EventThread 线程。

5.查询 Watcher。 EventThread 线程收到事件后,会从 ClientWatchManager 管理器中查询出对应的 Watcher,针对 SyncConnected-None 事件,那么就直接找出步骤 2 中存储的默认 Watcher,然后将其放到 EventThread 的 waitingEvents 队列中去。

6.处理事件 EventThread 不断地从 waitingEvents 队列中取出待处理的 Watcher 对象,然后直接调用该对象的 process 接口方法,以达到触发 Watcher 的目的。

三、Chroot 特性

3.2.0 版本后,添加了 Chroot 特性,该特性允许每个客户端为自己设置一个命名空间。如果一个客户端设置了 Chroot,那么该客户端对服务器的任何操作,都将会被限制在其自己的命名空间下。

通过设置 Chroot,能够将一个客户端应用于 Zookeeper 服务端的一颗子树相对应,在那些多个应用公用一个 Zookeeper 进群的场景下,对实现不同应用间的相互隔离非常有帮助。

四、会话

会话(Session)是 ZooKeeper 中最重要的概念之一,客户端与服务端之间的任何交互操作都与会话息息相关,这其中就包括临时节点的生命周期、客户端请求的顺序执行以及 Watcher 通知机制等。 在 前面小节中,我们已经讲解了 ZooKeeper 客户端与服务端之间一次会话创建的大体过程。以 Java 语言为例,简单地说,ZooKeeper 的连接与会话就是客户端通过实例化 ZooKeeper 对象来实现客户端与服务器创建并保持 TCP 连接的过程。

会话相关知识点:

(1)sessionID∶会话 ID

(2)zookeeper采用分桶策略管理会话,是将超时时间相近的会议放到同一个桶中来进行管理,以减少管理的复杂度。在检查超时的会话时,只需要检查桶中剩下的会话即可(没有被转移走的会话全是超时的)。

四、服务端启动(集群版)

1. 创建 ServerCnxnFactory。

2.初始化 ServerCnxnFactory。

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

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

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

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

7.恢复本地数据。

8. 启动 ServerCnxnFactory 主线程。

 

五、服务端leader选举

选举机制网上很多就不赘述了

https://www.cnblogs.com/shuaiandjun/p/9383655.html

简单说选举会通过投票方式优先选举事务id最大的服务器作为leader,如果事务id相同那么就选择服务器id(服务端初始化时递增生成的)最大的

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐