我的编程空间,编程开发者的网络收藏夹
学习永远不晚

关于ZooKeeper的会话机制Session解读

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

关于ZooKeeper的会话机制Session解读

一、为什么会有会话机制Session

ZooKeeper的架构图

首先我们看下ZooKeeper的架构图,client跟ZooKeeper集群中的某一台server保持连接,发送读/写请求,读请求直接由当前连接的server处理,写请求由于是事务请求,由当前server转发给leader进行处理。同时,client还能接收来自server端的watcher通知。

而所有的这些交互,都是基于client和ZooKeeper的server之间的TCP长连接,也称之为Session会话

ZooKeeper对外的服务端口默认是2181,客户端启动时,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测和服务器保持有效的会话,也能够向ZooKeeper服务器发送请求并接受响应,同时还能通过该连接接收来自服务器的Watch事件通知。

Session的SessionTimeout值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在SessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。

说点题外话,长连接、短连接、数据库连接池:

短连接 :连接->传输数据->关闭连接

也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。

长连接:连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。

长连接指建立SOCKET连接后不管是否使用都保持连接,但安全性较差。

网络中不同节点使用TCP协议通过SOCKET进行通信,首先需要3次握手建立连接,数据传输,4次握手断开连接,因此如果频繁的创建、关闭,是很耗费系统资源的,就像短连接那样;使用长连接貌似弥补了短连接的缺点,但是,如果并发量过大,会有大量的长连接,同样会耗费大量系统资源,因此具体选用长连接还是短连接,是要根据具体的场景来选择。

ZooKeeper中一个client只会跟一个server进行交互(除非与当前server连接失败,会切换到下个server),不管这种交互有多频繁,只需要一个TCP长连接就足以应对,因选择一个TCP长连接,不失为一种最好的方案。

数据库连接池:我们在使用JDBC进行数据库连接的时候,其实是建立了一个数据库连接池,它本身是一种短连接+长连接的方案,我们通过JDBC的3个关键配置来说明下:

参数名称参数说明默认值备注
minPoolSize连接池中保留的最小连接数5长连接
maxPoolSize连接池中保留的最大连接数15短连接
maxIdleTime最大空闲时间,如果超出空闲时间未使用,连接被收回

超过最小连接数后创建的连接,在最大空闲时间后如果未使用,是会被回收的,因此可以被理解为短连接。但是保留的最小连接数,即使未被使用也会一直存在,等待被使用,因此可以理解为长连接。

好了,扯了这么远,我们还是回到ZooKeeper是如何通过TCP长连接来管理它的Session会话的吧。

二、会话(Session)如何管理

2.1)SessionID的初始化

首先了解3个基本概念:

  • sessionID:会话ID,用来唯一标识一个会话,每次客户端创建会话的时候,ZooKeeper都会为其分配一个全局唯一的sessionID
  • TimeOut:会话超时时间,如果客户端与服务器之间因为网络闪断导致断开连接,并在TimeOut时间内未连上其他server,则此次会话失效,此次会话创建的临时节点将被清理
  • ExpirationTime:下次会话超时时间点。ZooKeeper会为每个会话标记一个下次会话超时时间点,便于对会话进行“分桶管理”,同时也是为了搞笑低耗的实现会话的超时检查与清理。其值接近于当前时间+TimeOut,但不完全相等,稍后会介绍。

在每次client向server发起“会话创建”请求时,服务端都会为其分配一个sessionID,现在看下sessionID是如何生成的。

在SessionTrackerImpl初始化的时候,会调用initializeNextSession来生成一个初始化的sessionID,之后在该sessionID的基础上为每个会话进行分配,其初始化算法如下:

//是ZooKeeper服务器的会话管理器,负责会话的创建、管理和清理等工作
public class SessionTrackerImpl extends Thread implements SessionTracker {
   
    {...}

	//参数id为当前服务器的myid
    public static long initializeNextSession(long id) {
        long nextSid = 0;
        //此处采用无符号右移,是为了防止出现负数的情况
        nextSid = (System.currentTimeMillis() << 24) >>> 8;
        nextSid =  nextSid | (id <<56);
        return nextSid;
    }
    
	{...}
}

该逻辑计算后得到的sessionID的前8位确定了所在的机器,后56位使用当前时间的毫秒表示进行随机。

2.2)分桶策略

SessionTrackerImpl通过**“分桶策略”来进行会话的管理,分桶的原则是将每个会话的“下次超时时间点”(ExpirationTime)**相同的会话放在同一区块中进行管理,以便于ZooKeeper对会话进行不同区块的隔离处理,以及同一区块的统一处理,如下图,横坐标是一个个的超时时间点ExpirationTime:

分桶管理

每个会话创建完毕后,ZooKeeper就会为其计算ExpirationTime,计算方式大体如下:

ExpirationTime = CurrentTime(当前时间) + SessionTimeOut(会话超时时间)

但图中标识的ExpirationTime并不是以上公式简单的算出来的时间。因为在ZooKeeper的实际实现中,还做了一个处理。

ZooKeeper的Leader服务器在运行期间会定时的进行会话超时检查,其时间间隔为ExpirationInterval(默认值2000毫秒),每隔2000毫秒进行一次会话超时检查。

为了方便同时对多个会话进行超时检查,完整的ExpirationTime计算方式如下:

ExpirationTime_ = CurrentTime + SessionTimeOut
ExpirationTime = ( ExpirationTime_/ExpirationInterval + 1 ) * ExpirationInterval

注意不要使用小学的乘法分配律把小括号给消化掉,它存在的目的就是为了保证ExpirationTime是ExpirationInterval的整数倍,那为什么要这样做???

提高会话检查的效率。让创建时间临近的会话,分配在一个桶中,实际生产环境中一个服务端会有很多客户端会话,逐个检查过期时间会非常耗时,把它们放在一个桶中批量处理,可以大大提高效率。

比如CurrentTime为1547046000、1547046001这样的会话就会被分配在一个桶中。

其次,Leader每隔ExpirationInterval 毫秒进行会话的清理,而刚好 ExpirationTime 这个时间点是会话的失效时间点,如果发现失效,直接清理掉就OK,避免了检查时未失效,但没过几毫秒又失效了这种情况。

比如,ExpirationTime 是1547046000,如果在1547045998的时刻检查,发现还有效,但过了2ms之后就无效了。而如果会话超时检查和会话超时时间在同一个时间节点的话,就会避免这种情况。

2.3)会话激活

为了保持client会话的有效性,在ZooKeeper运行过程中,client会在会话超时时间过期范围内向server发送PING请求来保持会话的有效性,俗称“心跳检测”。

同时server重新激活client对应的会话,这段逻辑是在SessionTrackerImpltouchSession中实现的。

先看下流程,再看源码:

会话激活

再看下源码实现:

//sessionId为发起会话激活的client的sessionId,timeout为会话超时时间
synchronized public boolean touchSession(long sessionId, int timeout) {
        
        SessionImpl s = sessionsById.get(sessionId);
        // Return false, if the session doesn't exists or marked as closing
        if (s == null || s.isClosing()) {
            return false;
        }
        //计算当前会话的下一个失效时间,可以理解为ExpirationTime_New
        long expireTime = roundToInterval(System.currentTimeMillis() + timeout);
        //tickTime是上一次计算的超时时间,可以理解为ExpirationTime_Old
        if (s.tickTime >= expireTime) {
            // Nothing needs to be done
            return true;
        }
        //将ExpirationTime_Old对应的桶中的会话取出,SessionSet 是SessionImpl的集合
        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);
        return true;
    }

好了,我们了解了是会话是如何激活的,那在什么时候会发起激活呢,也就是touchSession这个方法什么时候被触发呢?

分以下两种情况:

  • 只要client向server发送请求,包括读或写请求,就会触发一次激活;
  • 如果client发现在sessionTimeOut / 3 时间内未尚和server进行任何通信,就会主动发起一次PING请求,进而触发激活;

关于会话激活,可以举个非常脑洞的例子:就像你跟房东租房,进行续签一样。合同是一年一年的续签,这是理论情况下,但是中间免不了要跟房东打交道,比如洗衣机坏了,问问房东如何处理,这一问,糟了,从问的这一天开始,重新签一年的合同吧(当然是把之前的租金结算一下);另外一种就是 租期一年 / 3 = 每个季度,主动的跟房东续签下合同(当然也是把之前的租金结算一下)……

租房伤不起啊,个税申报抵消房租,房东还不愿意[此处一个欲哭无泪的表情]

三、过期会话(Session)如何清理

一言蔽之吧,会话过期后,集群中所有server都删除由该会话创建的临时节点(EPHEMERAL)信息

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

关于ZooKeeper的会话机制Session解读

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

关于ZooKeeper的会话机制Session解读

这篇文章主要介绍了关于ZooKeeper的会话机制Session解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-02-15

关于ArrayList的动态扩容机制解读

这篇文章主要介绍了关于ArrayList的动态扩容机制解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-11-13

如何模仿J2EE的session机制实现App后端会话信息管理

这篇文章主要为大家展示了“如何模仿J2EE的session机制实现App后端会话信息管理”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何模仿J2EE的session机制实现App后端会话信息管
2023-05-30

web前端:关于javascript的事件执行机制理解

编程学习网:Javascript最初受java启发而开始设计的,目的之一就是“看上去像Java”,因此语法上有类似之处,一些名称和命名规范也借自Java。
web前端:关于javascript的事件执行机制理解
2024-04-23

“由于这台计算机没有终端服务器客户端访问许可证远程会话终段”的解决方法

由于Windows2003默认仅支持2个终端用户的登陆。当“终端连接超出了最大连接”的情况出现导致不能登录时,可以:1、在另外一台Windows2003的机器上运行“tsmmc.msc”,打开
2023-05-25

如何解决由于这台计算机没有终端服务器客户端访问许可证远程会话终段的问题

如何解决由于这台计算机没有终端服务器客户端访问许可证远程会话终段的问题,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。由于windows2003默认仅支持2个终端用户的登陆。当“
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录