ZooKeeper会话的创建过程

会话创建过程中ZooKeeper客户端的工作流程

  客户端一次会话创建的过程可以分为三个阶段:初始化阶段、会话创建阶段、响应处理阶段。

初始化阶段

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

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

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

4.创建并初始化客户端网络连接器:ClientCnxn
  ZooKeeper客户端首先会创建一个网络连接器ClientCnxn,用来管理客户端与服务器的网络交互。客户端在创建ClientCnxn的同时,还会初始化客户端两个核心队列outgoingQueue和pendingQueue,分别作为客户端的请求发送队列和服务端响应的等待队列。

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

会话创建阶段

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

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

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

9.构造ConnectRequest请求
  SendThread负责根据当前客户端的实际设置,构造出一个ConnectRequest请求,该请求代表了客户端试图与服务器创建一个会话。同时,ZooKeeper客户端还会进一步将该请求包装成网络I/O层的Packet对象,放入请求发送队列outgoingQueue中去。

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

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

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

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

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

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

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

会话创建过程中ZooKeeper服务端的工作原理

Session

  Session是ZooKeeper中的会话实体,代表了一个客户端会话。包含以下4个基本属性。会话ID,用来唯一标识一个会话,每次客户端创建新会话的时候,ZooKeeper都会为其分配一个全局唯一的sessionID

属性说明
sessionID会话ID,用来唯一标识一个会话
TimeOut会话超时时间
TickTime下次会话超时时间点
isClosing用于标记一个会话是否已经被关闭

SessionID
  sessionID用来唯一标识一个会话,因此ZooKeeper必须保证sessionID的全局唯一性。接下来看看sessionID是如何生成的。

  在SessionTracker初始化的时候,会调用initializeNextSession方法来生成一个初始化的sessionID,之后在ZooKeeper的正常运行过程中,会在该sessionID的基础上为每个会话进行分配,其初始化算法如下:

public static long initializeNextSession(long id){
	long nextSid = 0;
	nextSid = (System.currentTimeMillis() << 24) >>8;
	nextSid = nextSid | (id << 56);
	return nextSid;
}

 从上面的代码可以看出sessionID的生成大致可以分为下面5个步骤。
1.获取当前时间的毫秒表示。
2.将获取到的当前时间的毫秒值左移24位
3.再将其进行无符号右移8位
4.添加机器标识SID。参数id就是当前zookeeper服务器的SID值,也就是配置在myid文件中的值,其高位56位都是0,因而需要右移56位。
5.将步骤3和步骤4的数值进行“|”操作

  可以将这个算法概况为:高8位确定了所在机器,后56位使用当前时间的毫秒表示进行随机。

SessionTracker
 SessionTracker是ZooKeeper服务端的会话管理器,负责会话的创建、管理和清理等工作。每一个会话在SessionTracker内部都保留了三份,如下表所示:

名称说明
sessionsById用于根据sessionID来管理Session实体,一个HashMap<Long, SessionImpl>类型的数据结构
sessionsWithTimeout用于根据sessionID来管理会话的超时时间,一个ConcurrentHashMap<Long, Integer>类型的数据结构
sessionSets用于根据下次会话超时时间点来归档会话,便于进行会话管理和超时检查,一个HashMap<Long, SessionSet>类型的数据结构

创建连接

 服务器对于客户端的“会话创建”请求的处理,可以分为四个步骤:处理ConnectRequest请求、会话创建、处理器链路处理、会话响应。

1.由NIOServerCnxn来负责接收来自客户端的“会话创建”请求,并反序列化出ConnectRequest请求,然后根据ZooKeeper服务端的配置完成会话超时时间的协商。

2.SessionTracker会为该会话分配一个sessionID,并将其注册到sessionsById和sessionsWithTimeout中去,同时进行会话的激活。

3.该“会话请求”还会在ZooKeeper服务端的各个请求处理器之间进行顺序流转,最终完成会话的创建。

分桶策略

 会话管理主要由SessionTracker负责,采用分桶策略来管理。分桶策略就是指将类似的会话放在同一区块中进行管理,以便于ZooKeeper对会话进行不同区块的隔离处理以及同一区块的统一处理。如下图所示。
在这里插入图片描述
  ZooKeeper将所有的会话都分配在了不同的区块中,分配的原则是每个会话的“下次超时时间点”(ExpirationTime),指该会话最近一次可能超时的时间点,其计算方式如下:

ExpirationTime = CurrentTime + SessionTimeout

  在ZooKeeper的实际实现中,还做了一个处理。ZooKeeper的Leader服务器在运行期间会定时地进行会话超时检查,其时间间隔是ExpirationInterval,单位是毫秒,默认值是tickTime的值,即默认情况下,每隔2000毫秒会进行一次会话超时检查。完整的ExpirationTime的计算方式如下:

ExpirationTime_ = CurrentTime + SessionTimeout
ExpirationTime = (ExpirationTime_/ExpirationInterval+1) × ExpirationInterval

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

loser与你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值