Zookeeper会话管理
一,会话实体Session类
Session是一个接口,定义在SessionTracker中。
public interface Session{
long getSessionId();
int getTimeout();
boolean isClosing();
}
1,sessionId
sessionId是会话的唯一标识,客户端在创建会话时,SessionTracker会调用方法,生成一个全局唯一的sessionId。
2,timeout
会话超时时间,Zookeeper在创建客户端实例时,会传入一个参数sessionTimeout,这个就是会话超时时间。SessionTracker在创建会话时,会把这个超时时间发给服务端,服务端会根据这个超时时间,计算一个超时时间。
3,isClosing
这个属性是用来标识一个会话是否被关闭。
如果一个会话被服务端判断为失效关闭了,那么isClosing就会置为true。有什么作用呢?作用就是对于已经标识为关闭的会话,不再处理该会话的请求。防止出现这种情况:一个会话已经关闭了,但是由于网络原因或者其他原因,这个会话的请求又来了,这时候果断拒绝该请求。
二,会话sessionId是如何生成的
sessionId是一个唯一标识,Zookeeper是如何生成这个唯一标识的呢?
源码如下:
public static long initializeNextSession(long id) {
long nextSid = 0L;
nextSid = System. currentTimeMillis() << 24 >>> 8;
nextSid |= id << 56; //id值得是机器的sid
return nextSid;
}
很简单的一段代码,使用的是java的位操作,详细过程这里不再赘述,感兴趣的可以在网上看一下。我们这里只关注一个点,那就是,这个sessionId由2部分构成,一个是机器号,二进制的前8位,一个是当前系统时间,二进制的后面56位。
总得来说就是:
sessionId = 机器号 + 系统时间
我又想问个问题了:如果sessionId的生成算法是这样的话,那是不是意味着,集群中的服务端机器的最大数量是256,也就是2的8次方?
三,会话管理器SessionTracker
Zookeeper会话的核心是会话管理器SessionTracker。说到Zookeeper会话,我们就要说说分桶策略。
分桶策略是SessionTracker管理会话的一个关键策略。什么是分桶策略呢?
网上说法众说纷纭,看完,我懵了。好吧,我自己看源代码理解一下。
首先,第一点,这个桶是什么呢?应该是一个set集合,也就是this.SessionSet,这个类是一个内部类,定义在SessionTracker的实现类SessionTrackerImpl中。
什么是分桶?那意思就是有多个桶,都有哪些桶呢?
有三个桶,如下源码定义:
HashMap<Long, SessionTrackerImpl.SessionImpl> sessionsById = new HashMap();
HashMap<Long, SessionTrackerImpl.SessionSet> sessionSets = new HashMap();
ConcurrentHashMap<Long, Integer> sessionsWithTimeout = new HashMap();
接下来,我们要看看这三个桶都是存放什么数据的?
sessionsById:这个桶存放session的逻辑是这样的,如果新创建的session在这个桶中不存在,就会把创建的session放到这个桶里。
sessionSets:这个桶的存放逻辑就比较复杂了。
第一个条件:sessionsById中有记录,并且isClosing=false。
第二个条件:tickTime小于expireTime
第三个条件:根据tickTime在sessionSets中查询不到记录。
综合来看,这个桶里存放的就是未超时的会话。这个桶就是分桶策略的具体实现!
sessionsWithTimeout:这个桶存放session的逻辑是这样的,每次新创建session,就会把创建的session放到这个桶里。
接下来我们看看分桶策略到底是如何工作的?首先我们可以发现,sessionSets这个桶是根据过期时间ExpirationTime来进行删除会话操作的。其他两个桶都是根据sessionId来进行删除的。
根据过期时间ExpirationTime来进行删除,这里是重点,也就是说,这样可以实现批量删除多个会话的目的。这就是分桶策略。
好,接下来我们理解一下下面这段话。
《从Paxos到Zookeeper》一书中是这样描述的:
所谓分桶策略,是指将类似的会话放在同一区块中进行管理,以便于ZooKeeper对会话进行不同区块的格里处理以及同一区块的统一处理。
ZooKeeper将所有的会话都分配在了不同的区块之中,分配的原则是每个会话的“下次超时时间点”(ExpirationTime)。
结合上面的分析,我总结一下Zookeeper会话的分桶策略。
1,分桶是在sessionSets这个桶里进行的,其他两个桶和分桶策略没有关系。
2,HashMap<Long, SessionTrackerImpl.SessionSet> sessionSets = new HashMap();
sessionSets这个桶是一个HashMap,这个HashMap以过期时间为key,以SessionTrackerImpl.SessionSet集合为value的一个HashMap。
3,SessionTrackerImpl.SessionSet这个集合是一个Set集合,里面存放的就是所有过期时间相同的session会话id。
四,会话清理
会话清理主要流程如下:
1,标记会话状态为已关闭。
字段isClosing置为true。
2,发起会话关闭请求。
为了使关闭操作在整个集群中生效,Zookeeper使用了提交会话关闭请求的方式,并立即交给PrepRequestProcessor处理器进行处理。
3,收集需要清理的临时节点。
在Zookeeper中,一旦某个会话失效后,那么和该会话关联的临时节点都需要清理。因此,需要先把这些临时节点整理出来。如何整理呢?如何找到一个会话的所有临时节点呢?
在Zookeeper的内存数据库中,会为每个会话保存一份临时节点集合。只需要根据sessionId就可以获取到一个会话的所有临时节点。
4,添加节点删除事务变更。
5,删除临时节点。
FinalRequestProcessor负责最后的删除操作。
6,移除会话。
会话的节点都删除以后,接下来就是删除会话。SessionTracker会把该会话从集合中清理掉。主要是这三个集合:
sessionSets
sessionsWithTimeout
sessionsById
7,关闭NIOServerCnxn。
五,会话重连
当客户端与服务端的网络连接断开时,Zookeeper客户端会自动反复重连,直到重新连上Zookeeper集群中的一台服务端为止。
这里主要说一下两个点,首先客户端和服务端的连接是TCP长连接。其次服务端一旦收到客户端的心跳检测,都会重新执行会话激活的逻辑,因此在正常情况下,客户端会话是一直有效的。
网友评论