-
TimeOut:一次会话的超时时间,客户端在构造Zookeeper实例的时候,会配置一个sessionTimeOut参数用于指定会话的超时的时间。Zookeeper服务端会按照连接的客户端发来的TimeOut参数来计算并确定超时的时间
-
TickTime:下一次会话超时的时间点,为了方便Zookeeper对会话进行所谓的分桶策略进行管理,同时也可以实现高效的对会话的一个检查和清理。TickTime是一个13位的Long类型的数值,一般情况下这个值接近TimeOut,但是并不完全相等
-
isCloseing:用来标记当前会话是否已经处于被关闭的状态。如果服务端检测到当前会话的超时时间已经到了,就会将isCloseing属性标记为已经关闭,这样以后即使再有这个会话的请求访问也不会被处理
SessionID作为一个全局唯一的标识,我们可以来探究下Zookeeper是如何保证Session会话在集群环境下依然能保证全局唯一性的:
在sessionTracker初始化的时候,会调用initializeNextSession来生成session,算法大概如下:
-
public s ta tic long initializeNextSession(long id) {
-
long nextSid = 0;
-
nextSid = (System.currentTim eM illis() « 24) » 8;
-
nextSid = nextSid | (id « 56);
-
return nextSid;
-
} }
从这段代码,我们可以看到session的创建大概分为以下几个步骤:
1.获取当前时间的毫秒表示
我们假设当前System.currentTimeMills()获取的值是1380895182327,其64位二进制表示为:
00000000 00000000 00000001 01000001 10000011 11000100 01001101 11110111
2.接下来左移24位,我们可以得到结果:
01000001 100000011 11000100 01001101 11110111 00000000 00000000 00000000,可以看到低位已经把高位补齐,剩下的低位都使用了0补齐
3.右移8位,结果变成了:
00000000 01000001 100000011 11000100 01001101 11110111 00000000 00000000
4.计算机器码标识ID:
在initializeNextSession方法中,出现了一个id变量,这个变量就是生成的SID的值,而SID在部署的时候就是我们在myid中配置的值,一般是一个整数,假设此时的值为2,转为64位二进制表示:
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000010
此时发现高位几乎都是0,进行左移56位以后,得到值如下:
00000010 00000000 00000000 00000000 00000000 00000000 00000000 00000000
5.将前面第三步和第四步得到的结果进行 | 操作:
可以得到结果为:
00000010 01000001 10000011 11000100 01001101 11110111 00000000 00000000
这个时候我们可以得到一个单机中唯一的序列号ID,整个算法大概可以理解为,先通过高8位确定机器以后,后面的56位按照毫秒进行随机,可以看出来当前的算法!还是蛮严谨的,基本上看不出来什么明显的问题,但是其实也有问题的,其中我们可以看到,zk选择了当前机器时间内的毫秒作为基数,但是如果时间到了2022年4月8号以后, System . currentTimeMillis ()的值会是多少呢?
-
Date d = newDate(2022-1900 f 3,8);
-
System. out. p rin tln ( Long. toBinaryString(d .getTime()));
打印出来的结果为:
- 0000000000000000000000011000000000000100110000010000010000000000
接着我们左移24位以后会发现,这个时候的值依然是个负数,所以我们为了保证不会出现负数的情况,解决方案如下:
-
publicstaticlong initializeNextSession(long id { ) {
-
long nextSid = 0;
-
nextSid = (System.currentTim eM illis() « 24) > » 8;
-
nextSid = nextSid | (id « 56);
-
return nextSid;
-
} }
这样就可以避免生成的时候出现负数了
SessionTrackerSessionTracker是Zookeeper中的会话管理器,负责整个zk生命周期中会话的创建、管理和清理操作,而每一个会话在Sessiontracker内部都保留了三份,大体如下:
1.sessionsWithTimeout这是一个ConcurrentHashMap
2.sessionsById是一个HashMap
3.sessionsSets同样也是一个HashMap
创建会话的过程,大体可以分为几个步骤,分别是处理ConnectRequest请求、创建会话、处理器链路处理和响应,在zk服务端中,首先是NIOServerCnxn来负责接受来自客户端的会话创建请求,并且进行反序列化工作,然后开始分配超时时间。分配完毕后,会开始创建sessionId,并且将其注册到SessionsById和sessionsWithTimeOut,进行激活,这个时候就可以考虑处理流转。
会话管理Zookeeper中的会话管理主要是SesssionTracker负责的,内部使用了一个特殊的机制,称之为分桶策略,所谓分桶策略,其实是将类似的会话放在一个区块中进行管理,以便于zookeeper对会话进行不同区块的隔离以及同一区块的统一处理
从图中我们可以看到,所有的会话都分配在了不同的区块中,分配原则是每个会话的下个超时的时间点,ExpiractionTime是指最近一次可能过期的时间点,每一个会话的ExpiractionTime的计算方式如下:
ExpiractionTime = CurrentTime + SessionTimeout
但是不要忘记了,Zookeeper的Leader服务器在运行期间会定期检查是否超时,这个定期的时间间隔为ExpiractionInterval,单位是秒,默认情况下是tickTime的值,即2000毫秒进行一次检查,完整的ExpiractionTime的计算方式如下:
-
ExpirationTime_= CurrentTime+ SessionTimeout;
-
ExpirationTime= (ExpirationTime_/ Expirationlnterval+ 1) x Expirationlnterval;
同样的,在整个zookeeper运行过程中,客户端会在超时时间内向服务端发送PING请求来保持时效性,俗称心跳检测,而服务端在接受到了客户端的心跳请求后需要再次激活会话状态,这个过程称之为TouchSession,流程如下:
1.检验会话是否已经被关闭,Lea
【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】 浏览器打开:qq.cn.hn/FTf 免费领取
der会去检查会话是否被关闭,如果已经关闭,不会再去激活该会话
2.如果会话没有被关闭,则开始计算下一次的超时时间Expiration_New,而计算的过程则是使用上面的公式
3.计算完新的超时时间以后,会去获取会员原来的超时时间,并且根据时间来定位原来存放的区块
4.接着,从该区块中找到会话,进行会话迁移,放入新的Expiration_New对应的区块中,如图所示:
闭,则开始计算下一次的超时时间Expiration_New,而计算的过程则是使用上面的公式
3.计算完新的超时时间以后,会去获取会员原来的超时时间,并且根据时间来定位原来存放的区块
4.接着,从该区块中找到会话,进行会话迁移,放入新的Expiration_New对应的区块中,如图所示:



