0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

详解zookeeper的session管理机制

马哥Linux运维 来源:稀土掘金技术社区 2024-01-08 09:46 次阅读

使用过zookeeper的都知道,当我们使用zookeeper创建一个节点时,我们能选择节点的类型是“临时节点”还是“永久节点”。临时节点和永久节点的区别是,临时节点会在客户端断开连接时被删除,而永久节点无论客户端是否断开连接,都会保留。

临时节点非常重要,我们经常利用它来实现分布式锁、选举等等

而为了实现临时节点的功能,zookeeper服务端就势必要有一套高效的session管理机制,它能实现如下功能:当客户端session失效后,服务端能感知到,随后删掉客户端当前创建的临时节点,并通知给其他的客户端。这篇文章会深入探讨zookeeper的session管理机制。

zookeeper的心跳机制

为什么要有心跳机制

zookeeper底层支持两种网络库,一种是zookeeper基于NIO自己写的,一种是Netty。那么zookeeper能不能直接通过感知TCP连接是否断开来感知客户端连接是否断开呢?

答案是不能,原因有很多,个人觉得最重要的一点是,基于TCP连接来判断客户端是否存活是不靠谱的

这里举两个异常case 1、客户端进程直接crash了,还没来得及发送FIN报文,这种情况下,zookeeper在TCP这一侧是没办法及时感知到TCP连接已经失效了。(也就是说,由于zookeeper没收到client的FIN包,虽然client已经挂了,TCP侧还认为客户端还活着。只能等弱鸡keepalive了)

168ceda2-ad48-11ee-8b88-92fbcf53809c.jpg

2、客户端和服务端之间很久没有报文交互,TCP连接其实已经失效了。(失效原因很多,比如路由器出问题了,网络设备故障了等等)这时候,如果客户端发送报文给服务端,linux会进行重试,默认差不多要重试15分钟,才能感知到这个连接已经失效。

16acafb6-ad48-11ee-8b88-92fbcf53809c.jpg这里只是举个例子。实际来说,client和zookeeper的报文交互可能不会那么少。也就是第二种情况,客户端很久不给服务端发报文,要发了,才发现tcp连接已经有问题了,这种情况不太可能出现。(当然,选举这种场景是有可能出现这种情况的,后面会单独写文章分析这个case) 但是从上面两个例子,我们也不难得出结论,通过tcp连接的断开与否来感知客户端是否存活,似乎并不太靠谱

client发送心跳

因为tcp的“不靠谱”,zookeeper为了能够实现可靠的连接管理(也为了保活),选择自己实现心跳机制。

175356fe-ad48-11ee-8b88-92fbcf53809c.jpg

正如上图所示,client隔一段时间就会发送一个心跳报文给服务端,告诉zookeeper自己还活着,别把我连接关了。

注意:这里乱入了一个create报文,因为正常报文也算是一种“心跳”。反正,zookeeper只要能收到报文,就能知道客户端还活着。

服务端收到报文后,会更新session信息

这里带大家看看代码。很简单,收到报文后,会调用SessionTracker.touchSession()来更新session信息

sessionId是zookeeper为每一个连接分配的唯一id

176b9476-ad48-11ee-8b88-92fbcf53809c.jpg

管理session的难点在哪

zookeeper 管理session的需求分析

这里我们思考下zookeeper对于session管理的需求是什么?

1、zookeeper需要有个地方存session

2、当客户端一段时间没发生心跳时,zookeeper要能感知到

第一个问题比较简单,java提供了各式各样的集合,无论是Map或者是List理论上都能存session。由于我们一般是通过sessionId来找到关联的Session的,因此使用Map更合适点。Zookeeper也是这么做的protected final ConcurrentHashMap sessionsById这里sessionsById 就是zookeeper管理Session使用的容器

第二个问题看起来也很简单,客户端不是会发送心跳么?我给每一个session记录一个上一个报文到达时间,一旦收到新的报文,我就更新这个时间。然后我再不断地扫描每个session是否很久没收到报文不就行了?

如何检测Session失效?

1787d78a-ad48-11ee-8b88-92fbcf53809c.jpg按照上面的说法,收到一个报文,我就更新Session的“上次报文”字段。假设session失效时间是4秒,我就每隔4秒扫描一次session的集合,找出那个超过4秒没有新的报文的session不就行了?像上图一样,假设sessionTimeout是4秒,现在已经11点50分05秒了,理论上每个session上一个报文时间应该大于11点50分01秒。很明显,session1失效了。

定时任务的选型

既然要定时扫描,我们就需要跑一个定时任务。jdk本身也提供了很多的定时任务方案。不知道定时任务的同学可以参考这篇文章Java中定时任务的6种实现方式,你知道几种?- 掘金既然如此,我们完全可以使用jdk自带的定时任务,定时去扫描这个集合啊,这样不就能很轻易的找到失效的session了么?

这里有两个问题:1、每一个客户端和服务端的连接,sessionTimeout都是可配的。我们例子是4秒没收到报文,就认为连接失效。实际超时时间可能有1秒、2秒、3秒.... 所以如果用定时任务来实现,我们可能需要启动不止一个定时任务。2、jdk提供的定时任务不够灵活,什么意思呢。比如我设置的sessionTimeout是4秒,现在是11点。然后我在11点00分02秒就收到了一个心跳,那么下次检测时间应该变成11点00分06秒。而jdk的定时任务限定了,只能每隔4秒检测一次。比如:11点、11点00分04秒、11点00分08秒、这样检测下去。而如果用jdk的定时任务,我们只能简单的隔一段时间,检测一次。这里可以仔细体会下两者的差异。

想一想这些问题,是不是发现想实现一个高效的session管理机制是不是没那么简单。接下来我们看看,zookeeper是如何巧妙地实现session的管理。

zookeeper session管理机制

接下来就要介绍zookeeper的session管理机制了。

1、通过expiryMap存储过期时间与session集合的对应关系

首先,zookeeper内部有一个expiryMap

17a02a24-ad48-11ee-8b88-92fbcf53809c.jpg

非常简单,Key是过期时间,value是一个Set,里面放了一个个的Session。

举个例子:S1在11点50分02秒的key下面,表示如果11点50分02秒前没收到新的报文,就认为S1过期了。

2、当收到某条连接的报文时,更新expiryMap

拿上图的S1为例,它的sessionTimeout是4秒,在11点50分01秒收到报文。那么理论上下个session检测时间会是 11点50分05秒。

这里要说下expiryMap的第一个特征,它的key并不是随意一个时间。它会间隔一个固定的时间叫做expirationInterval,数值上它等于zookeeper的配置tickTime(默认配的2秒)

所以说,这里计算出11点50分05秒后,它会round下,round到11点50分06秒

如图所示:

17b94a0e-ad48-11ee-8b88-92fbcf53809c.jpg

S1在截止时间前更新了session,我们就要把它从旧的桶里移除,挪到新的桶里。

3、循环检测ExpiryMap

有个SessionTracker线程会循环检测这个expiryMap,找到最近的那个key对应的session集合,把他们全部都过期掉。

17dc0b98-ad48-11ee-8b88-92fbcf53809c.jpg就像上面的例子,一旦时间到了11点50分02秒,就把对应的session全部过期掉。

小结下

1、如果收到报文,会把session放到下一个过期桶里。2、SessionTracker会按次序,不断地取出过期的桶,把桶里的session全部过期掉(过期会删除临时节点,当然还有其他一系列操作) 3、zookeeper底层使用了非常简单的Map就实现了非常高效的Session管理机制。

session管理机制源码分析

接下来我们来看看源码

1、server端的心跳续约


//org.apache.zookeeper.server.ExpiryQueue#update
public Long update(E elem, int timeout) {
  //1、除了上面我们介绍的ExpiryMap,zookeeper内部还有一个elemMap,用于存放 Session -> 过期时间
  Long prevExpiryTime = elemMap.get(elem);
  long now = Time.currentElapsedTime();
  //2、收到心跳后,我们会计算session应该更新到哪个桶里
  Long newExpiryTime = roundToNextInterval(now + timeout);


  //桶不变,就不用更新expiryMap了
  if (newExpiryTime.equals(prevExpiryTime)) {
    // No change, so nothing to update
    return null;
  }


  // First add the elem to the new expiry time bucket in expiryMap.
  //3. 找到新的桶,插入进去
  Set set = expiryMap.get(newExpiryTime);
  if (set == null) {
    // Construct a ConcurrentHashSet using a ConcurrentHashMap
    set = Collections.newSetFromMap(new ConcurrentHashMap());
    // Put the new set in the map, but only if another thread
    // hasn't beaten us to it
    Set existingSet = expiryMap.putIfAbsent(newExpiryTime, set);
    if (existingSet != null) {
      set = existingSet;
    }
  }
  set.add(elem);


  // Map the elem to the new expiry time. If a different previous
  // mapping was present, clean up the previous expiry bucket.
  prevExpiryTime = elemMap.put(elem, newExpiryTime);
  //4. 从旧的桶里移除
  if (prevExpiryTime != null && !newExpiryTime.equals(prevExpiryTime)) {
    Set prevSet = expiryMap.get(prevExpiryTime);
    if (prevSet != null) {
      prevSet.remove(elem);
    }
  }
  return newExpiryTime;
}


其实就是这幅图 1、放入一个:Session -> 过期时间 的Map中 2、收到心跳后,我们会计算session应该更新到哪个桶里 3、找到新的桶,插入进去 4、把session从旧的桶里移除

2、SessionTracker不断地轮训,找到过期的Session集合,然后都过期掉

//org.apache.zookeeper.server.SessionTrackerImpl#run
@Override
public void run() {
  try {
    while (running) {
      //1. 这个其实就是不断地轮训下一个要检测的key
      // 比如按我们的例子,应该是11点50分02秒检测一次、11点50分04秒检测一次、11点50分06秒检测一次...
      // 这里的waitTime就是找到下次检测需要等待多久,比如现在是11点50分01秒了,这个waitTime就是1秒
      // 如果是11点50分02秒了,waitTime就是0,我们要开始把过期的session都失效掉了
      long waitTime = sessionExpiryQueue.getWaitTime();
      if (waitTime > 0) {
        Thread.sleep(waitTime);
        continue;
      }


      //2. 取出过期的Session集合,全部都expire掉,如果session都及时发送了心跳了,这里就会拿到一个空的集合
      for (SessionImpl s : sessionExpiryQueue.poll()) {
        ServerMetrics.getMetrics().STALE_SESSIONS_EXPIRED.add(1);
        setSessionClosing(s.sessionId);
        expirer.expire(s);
      }
    }
  } catch (InterruptedException e) {
    handleException(this.getName(), e);
  }
  LOG.info("SessionTrackerImpl exited loop!");
}


17dc0b98-ad48-11ee-8b88-92fbcf53809c.jpg其实就是这个图

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 路由器
    +关注

    关注

    22

    文章

    3705

    浏览量

    113513
  • TCP
    TCP
    +关注

    关注

    8

    文章

    1348

    浏览量

    78979
  • 客户端
    +关注

    关注

    1

    文章

    289

    浏览量

    16659
  • zookeeper
    +关注

    关注

    0

    文章

    33

    浏览量

    3663

原文标题:深入详解zookeeper的session管理机制

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    控制器中如何设计MMU--虚拟内存管理机制

    控制器中如何设计MMU--虚拟内存管理机制
    发表于 12-15 09:53

    安卓应用商店和APP市场管理机制

    “工信部正在搭建移动应用软件认证和管理服务,以加强对智能终端的安全管理。”工信部相关负责人昨日表示,目前为了解决智能终端的安全问题,内部确实已经明确了需要一套对安卓应用商店和APP市场管理机制,包括上线前的审核和上线后的监测和抽
    发表于 07-15 07:41

    Keil C动态内存管理机制分析及改进,不看肯定后悔

    Keil C动态内存管理机制分析及改进,不看肯定后悔
    发表于 04-25 08:48

    怎么给RTOS动态分区内存管理机制进行优化?

    怎么给RTOS动态分区内存管理机制进行优化?
    发表于 04-28 06:17

    嵌入式系统所用到的内存管理机制主要有哪几种

    嵌入式系统所用到的内存管理机制主要有以下两种: 1、虚拟内存管理机制: 有一些嵌入式处理器提供了MMU,在MMU具备内存地址映射和寻址功能,它使操作系统的内存管理更加方便。如果存在MMU ,操作系统
    发表于 12-17 06:34

    命令终端的常用操作有哪些?软件包管理机制是什么

    一.命令终端(terminal)常用操作:ctrl+shift +t,在终端上打开多个标签ctrl+alt+t,在终端上打开另外一个终端二.deb软件包管理1.Debian 软件包的管理机制 Deb
    发表于 12-21 06:38

    VxWorks内存管理机制的分析与研究

    实时性、可靠性是嵌入式开发对内存管理的基本要求,本文探讨了操作系统内存管理的主要问题,对嵌入式操作系统Vxworks 的内存管理机制进行分析,给出了Vxworks 高效内存管理
    发表于 01-07 12:35 23次下载

    linux内存管理机制浅析

    本内容介绍了arm linux内存管理机制,详细说明了linux内核内存管理,linux虚拟内存管理,arm linux内存管理等方面的知识
    发表于 12-19 14:09 73次下载
    linux内存<b class='flag-5'>管理机制</b>浅析

    TMS320F28x 事件管理机制参考

    DSP之TMS320F28x事件管理机制参考,很好的DSP自学资料,快来学习吧。
    发表于 04-15 14:48 15次下载

    最全SPARK内存管理机制

    最全SPARK内存管理机制
    发表于 09-08 14:17 5次下载
    最全SPARK内存<b class='flag-5'>管理机制</b>

    嵌入式系统内存管理机制详解

    操作系统的内存管理功能用于向操作系统提供一致的地址映射功能和内存页面的申请、释放操作。在嵌入式实时系统中,内存管理根据不同的系统,有不同的策略,对于有些系统支持的虚拟内存管理机制,对于另外一些系统,可能只有flat式的简单内存
    发表于 11-18 09:41 4493次阅读

    浅析物理内存与虚拟内存的关系及其管理机制

    本文主要介绍内存管理机制:物理内存与虚拟内存的关系,Linux内存管理机制,Python内存管理机制,Nginx内存管理机制,环形缓冲区机制
    的头像 发表于 04-12 09:55 5365次阅读
    浅析物理内存与虚拟内存的关系及其<b class='flag-5'>管理机制</b>

    xenomai系统中的xnheap管理机制

    本文分析的enomai系统中的内存池(xnheap)管理机制
    的头像 发表于 05-25 10:15 1693次阅读

    zookeeper引入什么机制

    Zookeeper是一个开源的分布式协调服务,被广泛应用于构建分布式系统和大规模集群的管理。作为一个分布式协调服务,Zookeeper引入了一系列机制来提供可靠的协调和一致性服务。在这
    的头像 发表于 12-03 16:38 801次阅读

    zookeeper的选举机制

    ZooKeeper是一个分布式协调服务,主要用于管理分布式系统中的配置信息、命名服务、分布式锁和分布式队列等。在ZooKeeper集群中,为了保证高可用性,需要选举出一个主节点(Leader),其他
    的头像 发表于 12-04 10:39 965次阅读