Tocmat集群中最重要的交换信息就是会话消息,对某个tomcat实例某会话做的更改要同步到集群其他tomcat实例的该会话对象,这样才能保证集群所有实例的会话数据一致。在tribes组件的基础上完成这些工作就相当容易些,tribes是tomcat实现的一个通信框架。

如下图,tomcat实现会话同步的过程中大致会使用如下组件,现在假设中间的tomcat实例的会话改变了,它会通过会话管理器Manager将改变的动作消息封装成消息然后调用集群对象Cluster,通过Cluster将消息发送出去,同时Cluster又依赖于tribes,最后消息其实是交由tribes真正发送的,通信过程是以ClusterMessage为对象传输的,它会先被序列化进行传输,到达左边和右边的tomcat实例时会被反序列化,消息由tribes接收后往Cluster上传,最后到达会话管理器Manager,Manager根据动作消息去同步会话。

所以Cluster其实就是实现了ChannelListener的监听类,当tribes接收到消息后就会调用此监听器的messageReceived方法处理逻辑,此方法又会继续往上通知Manager的messageDataReceived方法,此方法内完成会话同步处理逻辑。关于会话具体的同步机制tomcat提供了两种,分别是“集群增量会话管理器——DeltaManager”和“集群备份会话管理器——BackupManager”。

  • DeltaManager会话管理器

DeltaManager会话管理器是tomcat默认的集群会话管理器,它主要用于集群中各个节点之间会话状态的同步维护。集群增量会话管理器的职责是将某节点的会话该变同步到集群内其他成员节点上,它属于全节点复制模式,所谓全节点复制是指集群中某个节点的状态变化后需要同步到集群中剩余的节点,非全节点方式可能只是同步到其中某个或若干节点。在集群中全节点会话复制的一个大致步骤如下图所示,客户端发起一个请求,假设通过一定的负载均衡设备分发策略分到其中一个结点node1,如果还未存在session对象的话web容器将会创建一个会话对象,接着执行一些逻辑处理,在对客户端响应之前有个重要的事情是要把session对象同步到集群中其他节点上,最后再响应客户端。当客户端第二次发起请求时,假如分发到node3节点上,由于同步了node1的session会话,所以在执行逻辑时并不会取不到session的值。如果删除某个会话对象则要同时通知其他节点把相应会话删除,如果修改了某个会话的某些属性也同样要更新到其他节点的会话中。

DeltaManager其实就是一个会话同步通信解决方案,除了具备上面提到的全节点复制外,它还有具有只复制会话增量的特性,增量是以一个完整请求为周期,即会将一个请求过程中所有会话修改量在响应前进行集群同步。往下看Tomcat具体实现方案。

为区分不同的动作必须要先定义好各种事件,例如会话创建事件、会话访问事件、会话失效事件、获取所有会话事件、会话增量事件、会话ID改变事件等等,实际上tomcat集群会有9种事件,集群根据这些不同的事件就可以彼此进行通信,接收方对不同事件做不同的操作。如下图,例如node1节点创建完一个会话后,即向其他三个节点发送EVT_SESSION_CREATED事件,其他三个节点接收到此事件后则各自在自己本地创建一个会话,会话包含了两个很重要的属性——会话ID和创建时间,这两个属性都必须由node1节点跟着EVT_SESSION_CREATED一起发送出去,本地会话创建成功后即完成了会话创建同步工作,此时你通过会话ID查找集群中任意一个节点都可以找到对应的会话。同样对于会话访问事件,node1向其他节点发送EVT_SESSION_ACCESSED事件及会话ID,其他节点根据会话ID找到对应会话并更新会话最后访问时间,以免被认为是过期会话而被清理。类似的还有会话失效事件(同步集群销毁某会话)、会话ID改变事件(同步集群更改会话ID)等等操作。

Tomcat使用SessionMessageImpl类定义了各种集群通信事件及操作方法,在整个集群通信过程中就是按照此类定义好的事件进行通信,SessionMessageImpl包含的事件如下{ EVT_SESSION_CREATED、EVT_SESSION_EXPIRED、EVT_SESSION_ACCESSED、EVT_GET_ALL_SESSIONS、EVT_SESSION_DELTA、EVT_ALL_SESSION_DATA、EVT_ALL_SESSION_TRANSFERCOMPLETE、EVT_CHANGE_SESSION_ID、EVT_ALL_SESSION_NOCONTEXTMANAGER },除此之外它继承了序列化接口(方便序列化)、集群消息接口(集群的操作)、会话消息接口(事件定义及会话操作)。

集群增量会话管理器DeltaManager可以说是通过SessionMessageImpl消息来管理DeltaSession,即根据SessionMessageImpl里面的事件响应不同的操作。Tomcat的集群通信使用的是tribes组件(相关章节会对tribes组件详细分析),网络IO都交由tribes后应用可以更专注逻辑处理,DeltaManager存在一个messageDataReceived(ClusterMessage cmsg)方法,此方法会在本节点接收到其他节点发送过来的消息后被调用,且传入的参数为ClusterMessage类型,可转化为SessionMessage类型,然后根据SessionMessage定义的9种事件做不同处理。其中有一个事件需要关注的是EVT_SESSION_DELTA,它是对会话增量同步处理的事件,某个节点在一个完整的请求过程中对某会话相关属性的所有操作被抽象到了DeltaRequest对象中,而DeltaRequest被序列化后会放到SessionMessage中,所以EVT_SESSION_DELTA事件处理逻辑就是从SessionMessage获取并反序列化出DeltaRequest对象,再将DeltaRequest包含的对某个会话的所有操作同步到本地该会话中,至此完成会话增量同步。

总的来说DeltaManager就是DeltaSession的管理器,它提供了会话增量的同步方式而不是全量同步,极大提高了同步效率。

  • 集群备份会话管理器——BackupManager

全节点复制的网络流量随节点数量增加呈平方趋势增长,也正是因为这个因素导致无法构建较大规模的集群,为了使集群节点能更加大,首要解决的就是数据复制时流量增长的问题,于是tomcat提出了另外一种会话管理方式,每个会话只会有一个备份,它使会话备份的网络流量随节点数量的增加呈线性趋势增长,大大减少了网络流量和逻辑操作,可构建较大的集群。

下面看看这种方式具体的工作机制,集群一般是通过负载均衡对外提供整体服务,所有节点被隐藏在后端组成一个整体。前面各种模式的实现都无需负载均衡协助,所以图中都把负载均衡省略了。最常见的负载方式是前面用apache拖所有节点,它支持将类似“326257DA6DB76F8D2E38F2C4540D1DEA.tomcat1”的会话id进行分解,定位到tomcat集群中以tomcat1命名的节点上(这种方式称为Session Stick,由apache jk模块实现)。每个会话存在一个原件和一个备份,且备份与原件不会保存在同一个节点上,如下图,例如当客户端发起请求后通过负载均衡被分发到tomcat1实例节点上,生成一个包含.tomcat1后缀的会话标识,并且tomcat1节点根据一定策略选出此次会话对象备份的节点,然后将包含了{会话id,备份ip}的信息发送给tomcat2、tomcat3、tomcat4,如图中虚线所示,这样每个节点都有一个会话id、备份ip列表,即每个节点都有每个会话的备份ip地址。

完成上面一步后就是将会话内容备份到备份节点上,假如tomcat1的s1、s2两个会话的备份地址为tomcat2,则把会话对象备份到tomcat2中,类似的有tomcat2把s3会话备份到tomcat4,tomcat4把s4、s5两个对话备份到tomcat3,这样集群中所有的会话都已经有了一份备份。当tomcat1一直不出故障,由于Session Stick技术客户端将一直访问到tomcat1节点上,保证一直能获取到会话。而当tomcat1出故障了,这时tomcat也提供了一个failover机制,apache感知到后端集群tomcat1节点被移除了,这时它会把请求随机分配到其他任意节点上,接下去会有两种情况:

①刚好分到了备份节点tomcat2上,此时仍能获取到s1会话,除此之外,tomcat2还要另外做的事是将这个s1会话标记为原件且继续选取一个备份地址备份s1会话,这样一来又有了备份。

②假如分到了非备份节点tomcat3,此时肯定找不到s1会话,于是它将向集群所有节点发问,“请问谁有s1会话的备份ip地址信息?”,因为只有tomcat2有s1的备份地址信息,它接收到询问后应答告知tomcat3节点s1会话的备份在tomcat2,根据这个信息就能查到s1会话了,并且tomcat3在自己本地生成s1会话并标为原件,tomcat2上的副本不变,这样一来同样能找到s1会话,正常完整整个请求处理。

接着分析Tomcat对上面机制详细的实现,正常情况下为了支持高效的并发操作,tomcat的所有会话集使用ConcurrentHashMap

public Object put(Object key, Object value) {
  ①实例化MapEntry,将key和value传入,并设置源节点为目前节点。
  ②判断本地内存是否已包含key,如是则不仅要本地remove掉,还要跨节点remove。
  ③通过Round robin算法从MapMember中选择一个作为备份节点。
  ④实例化一个包含MSG_BACKUP标识的MapMessage对象并发送给备份节点。
  ⑤实例化一个包含MSG_PROXY标识的MapMessage对象并发送给除了备份节点外的其他(代理)节点。
  ⑥put进本地缓存。
}

其次,再看看它如何通过get实现获取会话对象操作:

public Object get(Object key) {
 ①获取本地的MapEntry对象,它或许直接包含了会话对象,或许包含了会话对象的存放位置信息。
 ②判断本节点是否属于源节点,如为源节点则直接获取MapEntry对象里面的会话对象并返回。
 ③判断本节点是否属于备份节点,若为备份节点则直接获取MapEntry对象里面的会话对象作为返回对象,并且还要将本节点升为源节点、重新选取一个新备份节点,把MapEntry对象拷贝到新备份节点。
 ④判断本节点是否属于代理节点,若为代理节点则向其他节点发送会话对象拷贝请求,“集群中谁有此会话对象请发送给我”,把接收到的会话对象放到本节点并作为返回对象,最后将本节点升为源节点。
}

最后,看看删除会话对象remove操作的实现:

public Object remove(Object key) {
 ①删除本地此MapEntry对象。
 ②广播其他节点删除此MapEntry对象。
}

通过上面三个方法已经很清晰描述了新的Map是如何进行跨节点的增删改查的,BackupManager会话管理器就是通过这个新的Map进行会话管理。


喜欢java的同学可以交个朋友:

Tomcat集群如何同步会话的更多相关文章

  1. Tomcat集群环境下session共享方案 通过memcached 方法实现

    对于web应用集群的技术实现而言,最大的难点就是:如何能在集群中的多个节点之间保持数据的一致性,会话(Session)信息是这些数据中最重要的一块.要实现这一点, 大体上有两种方式:一种是把所有Ses ...

  2. 在Nginx里指定ip_hash的方式解决Tomcat集群session的问题

    据称,Tomcat集群session同步方案有以下几种方式: 1)使用tomcat自带的cluster方式,多个tomcat间自动实时复制session信息,配置起来很简单.但这个方案的效率比较低,在 ...

  3. tomcat集群和负载均衡的实现(session同步)

      (一)环境说明 (1)服务器有4台,一台安装apache,三台安装tomcat (2)apache2.0.55.tomcat5.5.15.jk2.0.4.jdk1.5.6或jdk1.4.2 (3) ...

  4. window xp Apache与Tomcat集群配置--转载

    转载地址:http://www.cnblogs.com/obullxl/archive/2011/06/09/apache-tomcat-cluster-config.html 一. 环境说明 Win ...

  5. ngnix apache tomcat集群负载均衡配置

    http://w.gdu.me/wiki/Java/tomcat_cluster.html 参考: Tomcat与Apache或Nginx的集群负载均衡设置: http://huangrs.blog. ...

  6. linux+apache+mod_Jk+tomcat实现tomcat集群

    最近一段时间一直在研究实现apache + jk_mod + tomcat实现负载均衡,起初负载均衡算是配置蛮顺利的,但是到了配置tomcat集群时所有配置都没有问题,但是tomcat日志中一直提示没 ...

  7. apache、mod_jk负载均衡与tomcat集群

    最近需要搭建apache和tomcat的集群,实现静态网站直接通过apache访问,动态网站转交给tomcat处理,实现负载均衡和tomcat集群配置. apache安装 wget http://ap ...

  8. 【转】Tomcat集群Cluster实现原理剖析

    此文章来源:http://zyycaesar.iteye.com/blog/296606 此文章作者:zyycaesar 对于WEB应用集群的技术实现而言,最大的难点就是如何能在集群中的多个节点之间保 ...

  9. tomcat集群实现源码级别剖析

    随着互联网快速发展,各种各样供外部访问的系统越来越多且访问量越来越大,以前Web容器可以包揽接收-逻辑处理-响应整个请求生命周期的工作,现在为了构建让更多用户访问更强大的系统,人们通过不断地业务解耦. ...

随机推荐

  1. ●洛谷 P3616 富金森林公园

    题链: https://www.luogu.org/problemnew/show/3616 题解: 树状数组,,, 本题思路挺巧妙. 考虑这种暴力算法:(设H[i]为i位置的高度,水面的高度为B) ...

  2. hihocoder1258(水)(2015ACM/ICPC北京站)

    题意: 给你B,C,S三种模式,当出现S时直接得分最多300(即perfect) 当是B,C时后面会跟一个数字,当后面的数字是从1开始的连续时,直接得分最多300(即perfect) 问给你一系列,最 ...

  3. Python中模块之copy的功能介绍

    模块之copy的功能介绍 copy主要分两种: 1.浅拷贝 2.深拷贝 赋值: 在python中赋值算特殊的拷贝,其实赋值可以理解为同一个对象有两个名字,所以当其中一个发生变化,另一个也跟着会变化. ...

  4. javascript引擎任务运行顺序

  5. linux系统下安装jdk、tomcat、mysql、redis

    一,安装jdk 1.rpm -qa | grep jdk 检测是否安装jdk 2.rpm -e --nodeps 要卸载的已有jdk名称 3.tar -zxvf jdk-7u71-linux-i586 ...

  6. JVM常见问题 一(转载)

    1. 内存模型以及分区 JVM内存模型如下图所示: 此处我们集中注意中间绿色的部分,该部分为JVM的运行时内存,该部分包含了: 线程私有的(灰色): 程序计数器:记录执行到第几条指令 虚拟机方法栈:执 ...

  7. c++DLL编程详解

    DLL(Dynamic Link Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量.函数或类.在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代. ...

  8. Kafka生产者-向Kafka中写入数据

    (1)生产者概览 (1)不同的应用场景对消息有不同的需求,即是否允许消息丢失.重复.延迟以及吞吐量的要求.不同场景对Kafka生产者的API使用和配置会有直接的影响. 例子1:信用卡事务处理系统,不允 ...

  9. Xcode编译错误__NSCFConstantString

    __NSCFConstantString:主要错误就是数据类型造成的,然后就是检查哪个地方造成的数据类型调用错误 错误一:'-[__NSCFConstantString _imageThatSuppr ...

  10. 潜谈IT从业人员在传统IT和互联网之间的择业问题(上)-传统乙方形公司

    外包能去吗?项目型公司如何?甲方比乙方好?互联网公司就一定好吗? 相信许多从业者在经历了3-5年的工作期后都会带着这样的疑问或者疑惑. 2012年-2014年间,曾经面试过500人,亲身面试的也有15 ...