tomcat内部已经实现了集群功能,就算没有Apache的情况下,单独配置几个tomcat作为集群也是可行的,并非像大家理解的那样只有结合Apache才能实现集群(Apache只是做了下分发,也就是负载均衡)。tomcat用组播方式实现的节点之间session的拷贝,这里的组播区别于大家之前接触比较多的单播、广播方式,tomcat用组播方式来发现当前集群中的主机,然后用组播的方式传输session信息。如果对组播方式不了解的同学可以google下,这里不再介绍。
下边从配置文件处一点点讲解tomcat集群
1、增加jvmRoute属性
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat_node1">jvmRoute为路由标记,用来区分集群中的节点名称,如果你的集群中有2个以上的节点,请设置不同的节点名称。
2、增加集群配置
使用时将下边代码加到<Engine>节点中即可,不用做任何修改就可以测试了。
<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" mcastBindAddress="127.0.0.1" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" tcpListenAddress="127.0.0.1" 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>
下边一点点讲解配置中的节点
<Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/>Manager用来在节点间拷贝Session,默认使用DeltaManager,DeltaManager采用的一种all-to-all的工作方式,即集群中的节点会把Session数据向所有其他节点拷贝,而不管其他节点是否部署了当前应用。当集群中的节点数量很多并且部署着不同应用时,可以使用BackupManager,BackManager仅向部署了当前应用的节点拷贝Session。但是到目前为止BackupManager并未经过大规模测试,可靠性不及DeltaManager。
<MemberShip className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" droptTime="3000"/>Membership用于发现集群中的其他节点,这里的address用的是组播地址使用同一个组播地址和端口的多个节点同属一个子集群,因此通过自定义组播地址和端口就可将一个大的tomcat集群分成多个子集群。
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4000" autoBind="100" seceltorTimeout="5000" maxThreads="6"/>receiver用于各个节点接收其他节点发送的数据,在默认配置下tomcat会从4000-4100间依次选取一个可用的端口进行接收,自定义配置时,如果多个tomcat节点在一台物理服务器上注意要使用不同的端口。
<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 className="org.apache.catalina.tribes.group.GroupChannel">
Channel 是一个抽象的端口,和socket类似,集群member通过它收发信息。
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=""/>
Valve用于在节点向客户端响应前进行检测或进行某些操作,ReplicationValve就是用于检测当前的响应是否涉及Session数据的更新,如果是则启动Session拷贝操作,filter用于过滤请求,如客户端对图片,css,js的请求就不会涉及Session,因此不需检测,默认状态下不进行过滤,监测所有的响应。
在生产环境中使用以下选项:
<Valve className=”org.apache.catalina.ha.tcp.ReplicationValve” filter=”.*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;”/>
即当对静态页面图片等访问时不进行session replication。
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
设置此选项是,当一个节点crash时,访问跳到另一个节点,此时session ID 会将jvmRoute值和以前的session Id 绑定在一起想·
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster>
上边是对tomcat集群配置的详细配置,如果单用tomcat做集群的情况下,只需要将<MemberShip>中的address="238.0.0.4" 设为同一个就可以保证这些tomcat为一个集群中的不同主机,然后为不同的tomcat部署相同的应用就可以自动实现session在不同tomcat之间的复制了。
具体测试方法为:
1、在同一机器上部署两个tomcat,Web端口分别为18080,28080 ajp端口分别为18009、28009
2、将上边的集群配置<Cluster>加入到server.xml中(注意保证组播地址相同,这样2个tomcat才在一个集群中)
3、增加jvmRoute属性
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat_node1">
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat_node2">注意两个名字不要一样
4、在两个tomcat的webapps下加入WebProject项目,项目下边有一个index.jsp为初始页面,页面内容为:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@page import="java.util.*"%> <%@page import="java.net.InetAddress;"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Cluster App Test</title> </head> <body> <% InetAddress ip = InetAddress.getLocalHost(); //out.println(ip.getHostAddress()); %> This is responsed by <font color="red"> <%=ip.getHostAddress()%></font> <br> Host Name : <font color="red"><%=ip.getHostName()%></font> <br> Time : <font color="red"><%=new Date()%></font> <br> <% ip = null; %> <br /> <br /> <br /> <br /> Server Info: <% out.println(request.getLocalAddr() + " : " + request.getLocalPort() + "<br>"); %> <% out.println("<br>Session ID " + session.getId() + "<br>"); // 如果有新的 Session 属性设置 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 列表</b>"); Enumeration e = session.getAttributeNames(); while (e.hasMoreElements()) { String name = (String) e.nextElement(); String value = session.getAttribute(name).toString(); out.println(name + " = " + value + "<br>"); System.out.println(name + " = " + value); } %> <form action="index.jsp" method="POST"> 名称: <input type=text size=20 name="dataName"> <br> 值: <input type=text size=20 name="dataValue"> <br> <input type=submit> </form> </body> </html>
6、分别启动这两个tomcat
7、打开浏览器,输入http://localhost:18080/WebProject
8、在浏览器中打开一个新的页面,输入http://localhost:28080/WebProject
可以看到打开第二个页面时里边的session信息和前一个页面是一致的,这样就是tomcat集群中的session共享