Tomcat高可用Session集群

对于大型电商的架构来说,Session是不得不仔细考虑的一个点,而Tomcat作为大型架构中应用的比较多的应用来说,实现方案较之PHP,不知道好了多少。好了,废话不多说,先就上个干货。

Tomcat-Session保持常用方式:

StandardManager:

Tomcat7的默认会话管理器,针对非集群环境单台Tomcat服务器进行会话管理,当Tomcat正常关闭的时候,这些会话相关的数据会被写入磁盘上的一个名叫SESSION.ser的文件,并在Tomcat下次启动时读取此文件。正常关闭能持久,但是非正常关闭,Session会丢失。

PersistentManager

当一个会话长时间处于空闲状态时会被写入到swap中,这对于内存资源比较吃紧的应用环境来说比较有用(强烈建议自己测试)。可以将session存放在local file中,也可以存储在MySQL Server中。

DeltaManager:

用于Tomcat集群的会话管理器,就是所谓的session复制集群。工作模式是,当前服务器在保存session的时候还会将session同步到集群中其他的所有节点,只有2-3台Tomcat比较理想,多了就急剧消耗性能,不过这个是非常常用的。

BackupManager:

上面那种的改进版,也是session复制,但是不会复制到所有的节点,只会复制到指定的节点,需要结合pacemaker的资源粘性和倾向性,或者Rhcs的failover domain。会话还需要绑定在一个服务器,这种实现方式比较麻烦。有时间单独写一篇blog

Mysql Session Server:

使用Memcache做Session服务器

DeltaManager:

一、配置

1.在Tomcat配置的egine或者host中加入对应的官方DeltaManager配置:

配置文件位置:http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html,这个是Tomcat 7的,如果版本不同之需要将url中的tomcat-7-0改成对应的就好。对了,Tomcat 7版本的少了一些东西,具体在配置文件会指出

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
         channelSendOptions="8">
//Cluster:专用于配置Tomcat集群的元素,可用于Engine和Host容器中。在用于Engine
//容器中时,Engine中的所有Host均支持集群功能。

  //Manger:定义Session集群所使用的Manager,也就是上面介绍的那些集群方式
  <Manager className="org.apache.catalina.ha.session.DeltaManager"
           expireSessionsOnShutdown="false"
           notifyListenersOnReplication="true"/>

  //Channel:用于Cluster中给集群中同一组中的节点定义通信“信道”
  <Channel className="org.apache.catalina.tribes.group.GroupChannel">
    <Membership className="org.apache.catalina.tribes.membership.McastService"
    //Membership:用于Channel中配置同一通信信道上节点集群组中的成员情况,即监控加入当前集群
    //组中的节点并在各节点间传递心跳信息,而且可以在定长时间未接受到某成员的心跳信息时将其移除。
                address="228.0.0.4"   //组播地址
                port="45564"          //组播端口
                frequency="500"       //发送频率
                dropTime="3000"/>     //超时时间
    <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
    //Receiver:用于Channel定义某节点如何从其它节点的Sender接收复制数据
              address="auto"           //可以写IP地址
              port="4000"              //端口
              autoBind="100"           //同时允许100个连接
              selectorTimeout="5000"   //监测超时时间
              maxThreads="6"/>         //线程数

    //用于Channel中配置“复制信息”的发送器,实现发送Session至集群中的其它节点。
    <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
      <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
    </Sender>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
    <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
  </Channel>

  <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
         filter=""/>
  <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

  <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
            tempDir="/tmp/war-temp/"
            deployDir="/tmp/war-deploy/"
            watchDir="/tmp/war-listen/"
            watchEnabled="false"/>

  //就是下面这两句少了最后面的 "/"
  <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
  <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

2.在tomcat配置文件的engine段加入jvmRoute

<Engine name="Catalina" defaultHost="localhost" jvmRote="Tomcat21">

3.将默认App配置文件拷贝过来,然后进行修改

# mkdir /web/webapps/testapp/WEB-INF/
# cp /usr/local/tomcat/conf/web.xml /web/webapps/testapp/WEB-INF/
//编译安装是从这个地方拷贝,rpm安装请自行处理
# vim /web/webapps/testapp/WEB-INF/web.xml
 <distributable/>  //将这句加入其中,基本随便找个位置就行

4.前端调度器配置:   <Proxy balancer://tomcatCluster> BalancerMember http://192.168.100.21:8080 loadfactor=1 route=tomcat21 BalancerMember ajp://192.168.100.22:8009 loadfactor=1 route=tomcat22 </Proxy>

<VirtualHost *:80>
    ServerName itcys.top
    ProxyVia Off
    ProxyPreserveHost On
    ProxyPass ~ .*\.(html|css|js|peng|jpg|png|gif)$ !
    ProxyPass / balancer://tomcatCluster/
    ProxyPassReverse / balancer://tomcatCluster/
    <Location />
       Order Allow,Deny
       Allow from all 
    </Location>
</VirtualHost>

二、效果:

虽然服务器在不停的调度,但是Session还是不变。如果不是太消耗资源,这个无疑是一个非常好的Sessin集群方案。

StandardManager:

本来这个是自带的,应该放在最上面的,但是为了演示集群环境和非集群环境的不同,这个还是放在下面。

一.非集群环境测试

1.tomcat配置(只需要一台就好):

# cp /usr/local/tomcat/conf/server.xml  /usr/local/tomcat/conf/server.xml.bak
# vim /usr/local/tomcat/conf/server.xml
<!--
将Cluster段删除,或者用这样的方式注释掉
-->
# service tomcat restart

2.修改访问机器的hosts,将虚拟主机IP指向修改过了的Tomcat主机。

# sudo vim /etc/hosts

3.查看Session

4.重启Tomcat测试机器

# service tomcat restart

5.查看Session:

6.停止Tomcat,查看SESSION.ser文件

# service tomcat stop
# cat /usr/local/tomcat/work/Catalina/itcys.top/_/SESSIONS.ser
??srjava.lang.Integer⠤???8Ivaluexrjava.lang.Number???
                                                     ???xpsrjava.lang.Long;??̏#?Jvaluexq~V?
?sq~V?qsrjava.lang.Boolean? r?՜??Zvaluexpsq~sq~V? 30D7EE2352A6AB55C2A13DBAFBC00

上面那条PATH对应: $CATATINA_HOME/work/Catalina/<virtualHostName>/<webappName>/SESSIONS.ser

二.集群环境测试:

先查看Session:

# service tomcat stop   //停止21节点Tomcat

因为是Session复制集群,所以所有节点上面Session都相同,Session并无变化。

# service tomcat stop    //停止22节点Tomcat
# service tomcat start   //将2个节点的tomcat都启用

Session发生变化,效果为Session Cluster中,如果没有幸存的主机(保存所有Session的主机),Session就会被刷新。对应起来就是Session Manager只能存在一种。

memcache实现session保持:

http://repo1.maven.org/maven2/de/javakaffee/msm/

1.安装部署Memcache Server

因为使用Memcached的场景比较多,所以我就写了个ansible paly book。我的其他文章中应该写了memcached的安装方法,所以这里就不再提了。因为我这里没有给出安装方法,所以那个memcahed服务脚本文件也不提供了。其实究其原因还是因为我写的是blog,不是部署document。因为没有生产环境的经验,所以我希望读者借鉴我的blog就好,真正生产环境还是不要对着我的blog做,拿走思路和方法就行。

[root@Ansible ansible]# tree memcache/
memcache/
├── configFile
│   └── memcached
├── memcached.retry
├── memcached.yaml
└── packages
    ├── libevent-2.0.20-stable.tar.gz
    └── memcached-1.4.15.tar.gz

2 directories, 5 files
[root@Ansible ansible]# cat memcache/memcached.yaml 
# Sourece install memcached-1.4.15
# Author: Chen Yanshan  Mail: [email protected]  Blog: itcys.top
# Version 1.0   time: 2016-07-24
- hosts: memcacheServers
  remote_user: root
  vars:
      libevent: libevent-2.0.20-stable.tar.gz
      memcache: memcached-1.4.15.tar.gz
  tasks:
      - name: Copy libevent source file
        copy: src=/ansible/memcache/packages/ dest=/tmp/
      - name:  Unpack libevent source file
        shell: tar -xzvf /tmp/ creates=~/libevent-2.0.20-stable
      - name: Install plugin prerequisites
        yum: pkg= state=present
        with_items:
        - gcc
      - name: Configure
        shell: chdir=~/libevent-2.0.20-stable  ./configure --prefix=/usr/local/libevent create=~/libevent-2.0.20-stable/Makefile
      - name: Make and make install 
        shell: chdir=~/libevent-2.0.20-stable  make && make install create=/usr/local/libevent/bin/ 
      - name: Libevent so ldconfig
        shell: echo "/usr/local/libevent/lib" > /etc/ld.so.conf.d/libevent.conf&& /sbin/ldconfig creates=/etc/ld.so.conf.d/libevent.conf
      - name: Copy memecache source file
        copy: src=/ansible/memcache/packages/ dest=/tmp/
      - name: UnPack memcache source file
        command: tar -xzvf /tmp/
      - name: Configure
        shell: chdir=~/memcached-1.4.15 ./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent creates=/tmp/memcached-1.4.15/Makefile
      - name: Make and make install 
        shell: chdir=~/memcached-1.4.15 make && make install  
      - name: Copy memcached service scripts
        copy: src=/ansible/memcache/configFile/memcached dest=/etc/rc.d/init.d/ mode=0755
      - name: add chkconfig list
        command: chkconfig --add memcached

加入memcached之后。现在的架构就会变成这样:

MSM–memcached session manager是一个高可用的Tomcat session共享解决方案,除了可以从本机内存快速读取Session信息(仅针对黏性Session)外,同时可使用memcached存取Session,以实现高可用。 对于非黏性Session,memcached直接存储session。

黏性session原理介绍:安装在Tomcat上的MSM使用本机内存保存session,和StandardManager一样。另外,当一个请求结束时,session会被送回Memcached进行备份。当下一次请求开始时,本地Session可用,直接服务,请求结束后,session又被送回Memcached备份。   当集群中的一个Tomcat挂掉,下一次请求会被路由到其他Tomcat上。负责处理此此请求的Tomcat并不清楚Session的信息。此时它会从Memcached查找该Session,更新该Session并将其保存在本机内容。此次请求结束,session被修改,送回Memcached备份

memcached-session-manager项目地址,http://code.google.com/p/memcached-session-manager/ 安装文档:https://code.google.com/archive/p/memcached-session-manager/wikis/SetupAndConfiguration.wiki

先给依赖库(将这些包放到$CATALINA_HOME/lib/下面):

$ tree memcached-session-manager/
memcached-session-manager/
├── javolution-5.5.1.jar
├── memcached-session-manager-1.8.2.jar
├── memcached-session-manager-tc7-1.8.2.jar
├── msm-javolution-serializer-1.8.2.jar
└── spymemcached-2.10.2.jar

下载地址:http://repo1.maven.org/maven2/de/javakaffee/msm/ 如同下载地址中给出的的5个库一样,msm有5种序列化方式,所以下载的时候包的名称一定一定要对应,像上面tree种第三个包,tc7,这个也需要和tomcat版本对应。

Tomcat配置(就是之前的配置修改一些内容):

<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JasperListener" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">

      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
//------------------------------------------------------------------------------------
      <Host name="itcys.top"  appBase="/web/webapps"
            unpackWARs="true" autoDeploy="true">
        <Context path="" docBase="testapp" reloadable="true">
             <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
               memcachedNodes="n1:192.168.100.31:11211,n2:192.168.100.32:11211"
		//修改这个地方的配置
               failoverNodes="n1"
               requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
               transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"
                                              //javolution序列化方式,和上面给出的包一致
               />
        </Context>

        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="itcys.top_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
//------------------------------------------------------------------------------------
	  <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>

这个配置文件的含义就不解释了,配置内容很多,示例中给出的很少,有兴趣或者生产环境使用的是msm的朋友,建议去看下上面给出的官方文档,那里面解释的比较详细

测试(测试文件就直接使用之前提供了的):

过程我就不一一列出来了,反正来去就这2张图。但是Session保持和高可用都达到了,而且全部Tomcat同时关机再启动Session还是没有问题

基本上Tomcat Session HA就差不多了,虽然讲的不是很全面,但是我相信上面的内容对很多对Tomcat不是非常熟悉的运维工作者来说是肯定够用了,再讲下去也没太大的意义,看文档效果一样。