欢迎使用普元产品知识库,本知识库包含普元应用开发平台EOSPlatform,流程平台BPS,企业服务总线ESB,微服务平台Microservice,运维管理平台Devops,数据集成平台DI
欢迎使用普元文档库
本文描述如何搭建一个Tomcat集群环境,并且部署一个应用来验证集群的Session复制和失效转移。本次Tomcat集群基于Tomcat 6.0.43环境搭建。
同一台机器上搭建两个Tomcat6的server,变成一个集群:
集群的server1的conf/server.xml:
<Engine name="Catalina" defaultHost="localhost" jvmRoute="node1"> < ! < 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"> < ! < 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"> < ! < 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"> < ! < 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> ... ... |
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。