本文描述如何搭建一个Tomcat集群环境,并且部署一个应用来验证集群的Session复制和失效转移。本次Tomcat集群基于Tomcat 6.0.43环境搭建。

一、搭建Tomcat集群

 

all-to-all session复制,使用org.apache.catalina.ha.session.DeltaManager

同一台机器上搭建两个Tomcat6的server,变成一个集群:
集群的server1的conf/server.xml:

<Engine name="Catalina" defaultHost="localhost" jvmRoute="node1">
< !- chengy all-to-all cluster->
< Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/>
< Channel className="org.apache.catalina.tribes.group.GroupChannel">
< Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/>
< Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/>
< 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>
... ...

集群的server2的conf/server.xml:

<Engine name="Catalina" defaultHost="localhost" jvmRoute="node2">
< !- chengy all-to-all cluster->
< Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
< Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/>
< Channel className="org.apache.catalina.tribes.group.GroupChannel">
< Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/>
< Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" port="4001" autoBind="100" selectorTimeout="5000" maxThreads="6"/>
< 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>
... ...




图1: Tomcat的集群层次
默认的Session复制是all-to-all的session复制,即复制到相同多播地址和端口的server上,不管Server上是否包含集群的应用。但这种复制开销比较大,需要广播。为了减少广播的server可以如图1那样分成两个集群,Cluster1和Cluster2配置不同的多播地址和端口的组合,从而减少网络的广播包。
all-to-all使用DeltaManager执行Session复制。也可以使用BackupManager执行主备(primary-sendondary)方式的session复制(需要配置域拦截器)。
集群的成员关系使用多播心跳来建立。数据通讯通过TCP端口(默认为4000-4100)来进行。ReplicationValve用来发现请求是否完成和初始化复制。数据只有在session改变(setAttribute or removeAttribute)时进行复制。
同步复制和异步复制是通过channelSendOptions来设置的,8位异步复制,4位同步复制,由org.apache.catalina.tribe.Channel常量类定义。
集群消息监听器JvmRouteSessionIDBinderListener在一个节点失效时在集群的其他节点中重写session id。JvmRouteBinderValve重写session id保证下一个请求仍然是Session粘滞的。

二、主备模式的集群搭建

Server1的conf/server.xml摘要:

<Engine name="Catalina" defaultHost="localhost" jvmRoute="node1">
< !- primary-secondary session replication ->
< Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6">
< Manager className="org.apache.catalina.ha.session.BackupManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"
mapSendOptions="6"/>
< Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/>
< Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6"/>
< 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"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
< /Channel>
< Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
< Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"
filter=".\.gif;.\.js;.\.jpg;.\.png;.\.htm;.\.html;.\.css;.\.txt;"/>
< 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.ClusterSessionListener"/>
< /Cluster>
... ...


Server2的conf/server.xml摘要:

<Engine name="Catalina" defaultHost="localhost" jvmRoute="node2">
< !- primary-secondary session replication ->
< Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6">
< Manager className="org.apache.catalina.ha.session.BackupManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"
mapSendOptions="6"/>
< Channel className="org.apache.catalina.tribes.group.GroupChannel">
< Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/>
< Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto" port="4001" autoBind="100" selectorTimeout="5000" maxThreads="6"/>
< 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"/>
< Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
< /Channel>
< Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
< Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"
filter=".\.gif;.\.js;.\.jpg;.\.png;.\.htm;.\.html;.\.css;.\.txt;"/>
< 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.ClusterSessionListener"/>
< /Cluster>
... ...

 

三、测试session复制用的jsp

testCluster.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
< %@ page import="java.util.*" %>
< html><head><title>PAS Cluster Demo</title></head>
< body>
Server Info:
< %
out.println(request.getLocalAddr() + " : " + request.getLocalPort()+"<br>");%>
< %
out.println("<br> Session ID: " + session.getId()+"<br>");

String dataName = request.getParameter("dataName");
if (dataName != null && dataName.length() > 0) {
String dataValue = request.getParameter("dataValue");
session.setAttribute(dataName, dataValue);
}
out.print("<br><b>Session List</b><br>-------------------");
Enumeration<String> e = session.getAttributeNames();
while (e.hasMoreElements()) {
String name = e.nextElement();
String value = session.getAttribute(name).toString();
out.println( "<br>"+name + " = " + value);
System.out.println( name + " = " + value);
}
System.out.println();
%>
< br><br>
< form action="testCluster.jsp" method="POST">
Name:<input type=text size=20 name="dataName">
< br>
Value:<input type=text size=20 name="dataValue">
< br>
< input type=submit>
< /form>
< /body>
< /html>


如下图:使用apache访问testCluster.jsp, 不断添加session的属性,发现node1和node2不断变换,但session中的属性列表不断增加,说明session复制是成功的。注意:apache设置的不是session stiky。