说到zookeeper session管理 ,免不了要问

  • 什么是session?

  • session id/session是如何产生的?

  • session 信息如何存储?

本文以session tracker线程【详见SessionTrackerImpl】的运行机制作为主线,并尝试解答一些相关问题

1)session基础

在介绍session tracker线程之前先回答几个问题

1.1) 什么是session?

zookeeper中session意味着一个物理连接,客户端connect成功之后,会发送一个connect型请求,此时就会有session 产生(下面会具体讲)

1.2)sessionid是如何产生的?

在SessionTrackerImpl实例化的时候就会调用下面的函数【详见SessionTrackerImpl.initializeNextSession】

1
2
3
4
5
6
public static long initializeNextSession(long id) {
       long nextSid = 0;
       nextSid = (System.currentTimeMillis() << 24) >> 8;
       nextSid =  nextSid | (id <<56);
       return nextSid;
   }

产生的值会存入nextSessionId属性,以后一旦有新的连接(session)产生,就会nextSessionId++

1.3)session是如何产生的?

接到一个连接类型的请求【详见ZooKeeperServer.processConnectRequest】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int sessionTimeout = connReq.getTimeOut();
        byte passwd[] = connReq.getPasswd();
        int minSessionTimeout = getMinSessionTimeout();
        if (sessionTimeout < minSessionTimeout) {
            sessionTimeout = minSessionTimeout;
        }
        int maxSessionTimeout = getMaxSessionTimeout();
        if (sessionTimeout > maxSessionTimeout) {
            sessionTimeout = maxSessionTimeout;
        }
        cnxn.setSessionTimeout(sessionTimeout);
        // We don't want to receive any packets until we are sure that the
        // session is setup
        cnxn.disableRecv();
        long sessionId = connReq.getSessionId();
        if (sessionId != 0) {
            long clientSessionId = connReq.getSessionId();
            LOG.info("Client attempting to renew session 0x"
                    + Long.toHexString(clientSessionId)
                    " at " + cnxn.getRemoteSocketAddress());
            serverCnxnFactory.closeSession(sessionId);
            cnxn.setSessionId(sessionId);
            reopenSession(cnxn, sessionId, passwd, sessionTimeout);
        else {
            LOG.info("Client attempting to establish new session at "
                    + cnxn.getRemoteSocketAddress());
            createSession(cnxn, passwd, sessionTimeout);
        }

1.3.1)确定session的timeout和id

【详见SessionTrackerImpl.createSession】

1
2
3
4
synchronized public long createSession(int sessionTimeout) {
        addSession(nextSessionId, sessionTimeout);
        return nextSessionId++;
    }

可见产生session需要两个元素,一个是sessionid,一个是timeout

  • timeout由客户端确定,但必须在服务器规定的最大的timeout(ticktime*20)和最小的timeout(ticktime*2)之间

  • 如果客户端没有指定sessionid,那么就会产生一个新的session【详见ZooKeeperServer.createSession】,否则会reopen【详见ZooKeeperServer.reopenSession】

  • sessionid的产生上面解释过了

1.3.2)实例化session及相关关系存放

【详见SessionTrackerImpl.addSession】

1
2
3
4
5
6
7
8
9
10
sessionsWithTimeout.put(id, sessionTimeout);
        if (sessionsById.get(id) == null) {
            SessionImpl s = new SessionImpl(id, sessionTimeout, 0);
            sessionsById.put(id, s);
            if (LOG.isTraceEnabled()) {
                ZooTrace.logTraceMessage(LOG, ZooTrace.SESSION_TRACE_MASK,
                        "SessionTrackerImpl --- Adding session 0x"
                        + Long.toHexString(id) + " " + sessionTimeout);
            }
        }
  • 一个重要的数据结构sessionsWithTimeout存放sessionid和timeout的映射

  • 另一个重要的数据结构sessionsById存放sessionid和SessionImpl实例的映射

1.3.3)确定session实例的tickTime及sessionSets关系维护

【详见SessionTrackerImpl.touchSession】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
long expireTime = roundToInterval(System.currentTimeMillis() + timeout);
        if (s.tickTime >= expireTime) {
            // Nothing needs to be done
            return true;
        }
        SessionSet set = sessionSets.get(s.tickTime);
        if (set != null) {
            set.sessions.remove(s);
        }
        s.tickTime = expireTime;
        set = sessionSets.get(s.tickTime);
        if (set == null) {
            set = new SessionSet();
            sessionSets.put(expireTime, set);
        }
        set.sessions.add(s);
  • 根据当前时间和timeout计算本session 的expireTime即tickTime

  • 一个重要的数据结构sessionSets 存放过期时间和一组session实例(相同过期时间)的映射的建立及维护

  • session实例的tickTime的确定

2)session tracker线程的机制

在zookeeper服务体系中,专门有一个线程(session tracker)维护session【详见SessionTrackerImpl.run】,重要代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
currentTime = System.currentTimeMillis();
if (nextExpirationTime > currentTime) {
    this.wait(nextExpirationTime - currentTime);
    continue;
}
SessionSet set;
set = sessionSets.remove(nextExpirationTime);
if (set != null) {
    for (SessionImpl s : set.sessions) {
        sessionsById.remove(s.sessionId);
        expirer.expire(s);
    }
}
nextExpirationTime += expirationInterval;

可见SessionTrackerImpl这个线程会一直轮询的清除过期session

  • 每次轮询都会比较currentTime和nextExpirationTime,如果还未到nextExpirationTime,就等,否则往下走

  • 将sessionSets中的以nextExpirationTime为key的那组session移出

  • 遍历session,从sessionsById移除session,并调用相关的过期处理(下面会讲)

  • 调整下载比较的时间,即nextExpirationTime += expirationInterval;

3) session维护相关问题

3.1)清除session如何实现?

【详见ZooKeeperServer.close】

1
2
3
private void close(long sessionId) {
       submitRequest(null, sessionId, OpCode.closeSession, 0nullnull);
   }

3.1.1)构造一个Request实例

3.1.2)调用PrepRequestProcessor.processRequest放入submittedRequests队列

3.1.3)PrepRequestProcessor线程的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
request.hdr = new TxnHeader(request.sessionId, request.cxid, zxid,
                            zks.getTime(), type);
                                                    
switch (type) {
                                                   
    //省略N行代码......
                                                   
    case OpCode.closeSession:
        // We don't want to do this check since the session expiration thread
        // queues up this operation without being the session owner.
        // this request is the last of the session so it should be ok
        //zks.sessionTracker.checkSession(request.sessionId, request.getOwner());
        HashSet<String> es = zks.getZKDatabase()
                .getEphemerals(request.sessionId);
        synchronized (zks.outstandingChanges) {
            for (ChangeRecord c : zks.outstandingChanges) {
                if (c.stat == null) {
                    // Doing a delete
                    es.remove(c.path);
                else if (c.stat.getEphemeralOwner() == request.sessionId) {
                    es.add(c.path);
                }
            }
            for (String path2Delete : es) {
                addChangeRecord(new ChangeRecord(request.hdr.getZxid(),
                        path2Delete, null0null));
            }
                                                    
            zks.sessionTracker.setSessionClosing(request.sessionId);
        }
                                                    
        LOG.info("Processed session termination for sessionid: 0x"
                + Long.toHexString(request.sessionId));
        break;
      • 设置request.hdr,这个很重要,

        • 在FinalRequestProcessor.processRequest会有相应的处理

1
2
3
4
5
6
if (request.hdr != null) {
              TxnHeader hdr = request.hdr;
              Record txn = request.txn;
   
              rc = zks.processTxn(hdr, txn);
           }

    • 一旦某个session关闭,与session相关的EPHEMERAL类型的节点都得清除

    • 并且通过调用sessionTracker.setSessionClosing将session设置为关闭,使得后续此session上的请求无效

3.1.4)SessionTrackerImpl相关数据结构的清理

【详见SessionTrackerImpl.removeSession】

1
2
3
4
5
6
7
8
9
10
11
12
synchronized public void removeSession(long sessionId) {
    SessionImpl s = sessionsById.remove(sessionId);
    sessionsWithTimeout.remove(sessionId);
    if (LOG.isTraceEnabled()) {
        ZooTrace.logTraceMessage(LOG, ZooTrace.SESSION_TRACE_MASK,
                "SessionTrackerImpl --- Removing session 0x"
                + Long.toHexString(sessionId));
    }
    if (s != null) {
        sessionSets.get(s.tickTime).sessions.remove(s);
    }
}

分别对sessionsById、sessionsWithTimeout、sessionSets进行处理

3.2)session owner咋回事?

如果不是在集群环境,即没有LearnerHandler线程,session 的owner就是一个常量实例ServerCnxn.me

3.3)sessionsWithTimeout这个数据结构的用途?

sessionsWithTimeout存放的是sessionid和timeout,此数据结构会和ZKDatabase中相通,会被持久化

如果某个session timeout为60s,如果空闲了30s,意味着还能空闲30s,此时服务重启,那么此session的timeout又变为60s

3.4)touch session是干吗的?

每次一旦该session有请求,就会touch,意味着session的过期时间变为(基本等于当前时间+timeout)

具体算法为

1
2
3
4
private long roundToInterval(long time) {
        // We give a one interval grace period
        return (time / expirationInterval + 1) * expirationInterval;
    }

time为System.currentTimeMillis() + timeout

expirationInterval默认为ticktime

3.5)check session是干吗的

基本上所有的事务型操作,都会调用用来验证当前请求的session是否关闭,owner是否正确

4)小结

  • SessionTrackerImpl作为一个单独的线程专门处理过期session

  • SessionTrackerImpl有3个重要的数据结构sessionsById、sessionSets、sessionsWithTimeout,其中sessionsWithTimeout会被持久化

  • SessionTrackerImpl提供了几个常用的API

    • createSession

    • addSession

    • touchSession

    • removeSession

    • checkSession

    • setOwner

    • dumpSessions

zookeeper session tracker机制分析的更多相关文章

  1. Session Timer机制分析

    Session Timer机制分析 功能介绍 会话初始化协议(SIP)并没有为所建立的会话定义存活机制.尽管用户代理可以通过会话特定的机制判断会话是否超时,但是代理服务器却做不到这点.如此一来,代理服 ...

  2. 从session实现机制分析模拟请求验证码的可行性(转)

    悲剧了,发现写完这篇blog没有配上这个格调超高的标题.   1.0问题背景 现在要实现一个带验证码网站的的自动登陆功能.验证码识别过程不再这篇文章的讨论之中.(之后有篇文章我会详细的总结验证码的识别 ...

  3. 分布式session共享机制分析

    使用配置: 1.在pom文件中引入spring-session的jar包 <!--springsession--><dependency><groupId>org. ...

  4. Hadoop生态圈-Zookeeper的工作原理分析

    Hadoop生态圈-Zookeeper的工作原理分析 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   无论是是Kafka集群,还是producer和consumer都依赖于Zoo ...

  5. 分布式协调组件Zookeeper之 选举机制与ZAB协议

    Zookeeper简介: Zookeeper是什么: Zookeeper 是⼀个分布式协调服务的开源框架. 主要⽤来解决分布式集群中应⽤系统的⼀致性问题, 例如怎样避免同时操作同⼀数据造成脏读的问题. ...

  6. zookeeper节点Watch机制实例展示

    znode以某种方式发生变化时,“观察”(watch)机制可以让客户端得到通知.可以针对ZooKeeper服务的“操作”来设置观察,该服务的其他 操作可以触发观察. 实现Watcher,复写proce ...

  7. Linux信号(signal) 机制分析

    Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...

  8. ZooKeeper监听机制

    前言:Zookeeper的监听机制很多人都踩过坑,感觉实现了watcher 接口,后面节点的变化都会一一推送过来,然而并非如此. Watch机制官方声明:一个Watch事件是一个一次性的触发器,当被设 ...

  9. 【Zookeeper】源码分析目录

    Zookeeper源码分析目录如下 1. [Zookeeper]源码分析之序列化 2. [Zookeeper]源码分析之持久化(一)之FileTxnLog 3. [Zookeeper]源码分析之持久化 ...

随机推荐

  1. 字符串&数组的相互转换

    字符串 -> 数组 方法一: $str = "abcd" $s2 = $str.GetEnumerator()  #$s2是无法使用下标的方式进行索引的,因为其不是array ...

  2. jQuery中添加/改变/移除改变CSS样式例子

    在jquery中对于div样式操作我们会使用到CSS() removeClass() addClass()方法来操作了,下面我们就整理了几个例子大家一起来看看吧.     CSS()方法改变CSS样式 ...

  3. PHP 开发 APP 接口 学习笔记与总结 - APP 接口实例 [5] 版本设计分析及数据表设计

    APP 版本升级以及 APP 演示 ① 版本升级分析以及数据表设计 ② 版本升级接口开发以及 APP 演示 /** * version_upgrade 版本升级信息表 */ CREATE TABLE ...

  4. 使用spring等框架的web程序在Tomcat下的启动顺序及思路理清

    大牛请绕过,此文仅针对自己小白水平,对web程序的启动流程做个清晰的回顾. 一.使用spring等框架的web程序在Tomcat下的启动流程 1)Tomcat是根据web.xml来启动的.首先到web ...

  5. reduce()

    Professional.JavaScript.for.Web.Developers.3rd.Edition.Jan.2012 var value = [1,2,3,4,5]; var sum = v ...

  6. 二进制流 最后一段数据是最后一次读取的byte数组没填满造成的

    while(in.read(temp)!=-1){ out.write(temp); } 改成: int len; while((len=in.read(temp))!=-1){out.write(t ...

  7. Android Gradle 编译错误Java finished with non-zero exit value 2

    出现这个错误主要有两类错误 依赖包重复 方法数超过65K 针对第一种错误,可能是由于build.gradle里写了 compile fileTree(dir: 'libs', include: ['* ...

  8. nodejs 执行shell 命令

    有需要从前端操作服务器执行shell命令的需求 建立一个process.js文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var process =  ...

  9. phpexcel 读取数据

    最近公司做一个客户导入会员的功能,以前导入都是使用csv格式导入的,但是客户反应问题挺多的,普遍是乱码(由于各种系统各种环境可能引起编码问题).最近想着就把这个导入完全改成excel导入,就研究了下p ...

  10. TOMCAT源码分析(启动框架)

    建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动 ...