用tomcat做负载集群时, 经常会用到session复制(Session Replication), 很多例子会告诉我们要配置apache或者其他的Web Server. 而事实上, 单纯从session复制的角度讲, 是不需要Web Server的.
tomcat的session复制分为两种, 一种是全局试的(all-to-all), 这意味着一个node(tomcat实例)的session发生变化之后, 它会将这些变更复制到其他所有集群组的成员;另一种是局部试的, 它会用到BackupManager, BackupManager能实现只复制给一个Buckup Node, 并且这个Node会部署相同的Web应用, 但是这种方式并没用经过很多的测试(来自官方说明..).
tomcat的session复制是基于IP组播(multicast)来完成的, 详细的IP组播介绍可以参考这里.
简单的说就是需要进行集群的tomcat通过配置统一的组播IP和端口来确定一个集群组, 当一个node的session发生变更的时候, 它会向IP组播发送变更的数据, IP组播会将数据分发给所有组里的其他成员(node).
配置如下(这里所有的tomcat都在不同的物理主机, 如果在同一台主机上需要改tomcat的tcpListenPort
)
<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>
然后新建一个web应用, 我们这里叫TomcatClusterDemo, web context与名称一致.
新建一个jsp, 这个jsp复制向session里创建/更新属性, 并将session里的所有属性显示在网页上.
当然, 为了验证我们的session是同步的, 我们还将session ID显示了出来, 代码如下:
<%@ page contentType="text/html; charset=UTF-8" %> <%@ page import="java.util.*" %> <html><head><title>Tomcat Cluster Demo</title></head> <body> Server Info: <% out.println(request.getLocalAddr() + " : " + request.getLocalPort()+"<br>");%> <% out.println("<br> ID " + session.getId()+"<br>"); String dataName = request.getParameter("dataName"); if (dataName != null && dataName.length() > 0) { String dataValue = request.getParameter("dataValue"); session.setAttribute(dataName, dataValue); System.out.println("application:" + application.getAttribute(dataName)); application.setAttribute(dataName, dataValue); } out.print("<b>Session List</b>"); Enumeration<String> e = session.getAttributeNames(); while (e.hasMoreElements()) { String name = e.nextElement(); String value = session.getAttribute(name).toString(); out.println( name + " = " + value+"<br>"); System.out.println( name + " = " + value); } %> <form action="test.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>
同时, 在web.xml里增加<distributable/>描述
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>TomcatClusterDemo</display-name> <distributable/> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>test.jsp</welcome-file> </welcome-file-list> </web-app>
现在将TomcatClusterDemo部署到两个tomcat上(直接将war包或者部署文件拷贝到webapps下, 或者通过Tomcat Web Application Manager部署), 依次启动两个tomcat.
先访问第一台tomcat(下面的9.119.84.68)的test.jsp, 并添加几个session.
然后访问第二台tomcat(下面的9.119.84.88)的test.jsp, 为了告诉tomcat我们要用那个session访问, 我们需要在URL后面加上 ;jsessionid=SESSION_ID
SESSION_ID可以从第一个test.jsp页面上获得.
看看效果, 两个在不同server上的tomcat实现了session复制!