当SessionTracker的会话超时检查线程整理出一些已经过期的会话后,那么就要开始进行会话清理了。会话清理的步骤大致可以分为以下7步。
标记会话状态为“已关闭”
由于整个会话清理过程需要一段的时间,因此为了保证在此期间不再处理来自该客户端的新请求,SessionTracker会首先将该会话的isClosing属性标记为true。这样,即使在会话清理期间接收到该客户端的新请求,也无法继续处理了。
发起“会话关闭”请求
为了使对该会话的关闭操作在整个服务端集群中都生效,ZooKeeper使用了提交“会话关闭”请求的方式,并立即交付给PrepRequestProcessor处理器进行处理。
收集需要清理的临时节点
在ZooKeeper中,一旦某个会话失效后,那么和该会话相关的临时(EPHEMERAL)节点都需要被一并清除掉。因此,在清理临时节点之前,首先需要将服务器上所有和该会话相关的临时节点都整理出来。
在ZooKeeper的内存数据库中,为每个会话都单独保存了一份由该会话维护的所有临时节点集合,因此在会话清理阶段,只需要根据当前即将关闭的会话的sessionID从内存数据库中获取到这份临时节点列表即可。
但是,在实际应用场景中,情况并没有那么简单,有如下的细节需要处理:在ZooKeeper处理会话关闭请求之前,正好有以下两类请求到达了服务端并正在处理中。
- 节点删除请求,删除的目标节点正好是上述临时节点中的一个。
- 临时节点创建请求,创建的目标节点正好是上述临时节点中的一个。
对于这两类请求,其共同点都是事务处理尚未完成,因此还没有应用到内存数据库中,所以上述获取到的临时节点列表在遇上这两类事务请求的时候,会存在不一致的情况。
假定我们当前获取的临时节点列表是ephemerals,那么针对第一类请求,我们需要将所有这些请求对应的数据节点路径从ephemerals中移除以避免重复删除。针对第二类请求,我们需要将所有这些请求对应的数据节点路径添加到ephemerals中去,以删除这些即将会被创建但是尚未保存到内存数据库中去的临时节点。
添加“节点删除”事务变更
完成该会话相关的临时节点收集后,ZooKeeper会逐个将这些临时节点转换成“节点删除”请求,并放入事务变更队列outstandingChanges中去。
删除临时节点
在上面的步骤中,我们已经收集了所有需要删除的临时节点,并创建了对应的“节点删除”请求,FinalRequestProcessor处理器会触发内存数据库,删除该会话对应的所有临时节点。
移除会话
完成节点删除后,需要将会话从SessionTracker中移除。主要就是从上面提到的三个数据结构(sessionById、sessionsWithTimeout和sessionSets)中将该会话移除掉。
关闭NIOServerCnxn
最后,从NIOServerCnxnFactory找到该会话对应的NIOServerCnxn,将其关闭。