/**
* CentOS-6.4-minimal版中Apache-2.2.29与Tomcat-6.0.41实现集群
* ----------------------------------------------------------------------------------------------------------------------
* 本文建立在Apache-2.2.29与Tomcat-6.0.41实现负载均衡的基础上,实现过程详见http://blog.csdn.net/jadyer/article/details/39617801
* ----------------------------------------------------------------------------------------------------------------------
* 几个术语
* 1)负载均衡
* 前端服务器(常常名为"负载均衡器","代理均衡器"或"反向代理")收到HTTP请求后,将请求分发到后端的不止一个"worker"的web服务器,由它们实际处理请求
* 2)会话复制
* 会话复制(即常说的Session共享)是一种机制,将客户端会话的整个状态原原本本复制到集群中的两个或多个服务器实例,以实现容错和故障切换功能
* 3)集群
* 集群由两个或多个Web服务器实例组成,这些服务器实例步调一致地工作,透明地处理客户端请求,客户端将一组服务器实例认为是单一实体服务
* ----------------------------------------------------------------------------------------------------------------------
* 几个区别
* 1)集群有别于分布式的解决方案,它采用的是每台服务器运行相同应用的策略,由负责均衡的服务器进行分流,这可以提高整个系统的并发量及吞吐量
* 2)由于集群服务需要在处理请求之间不断地进行会话复制,复制后的会话将会慢慢变得庞大,因此它的资源占用率是非常高的
* 如果在并发量大的应用中,复制的会话大小会变得相当大,而使用的总内存更是会迅速升高
* 3)集群的会话复制,增加了系统的高可用性,由于在每台服务器都保存有用户的Session信息
* 如果服务器群中某台宕机,应用可以自动切换到其它服务器上继续运行,而用户的信息不会丢失,这提高了应用的冗错性
* 4)实践证明,在各应用服务器之间不需要状态复制的情况下,负载均衡可以达到性能的线性增长及更高的并发需求
* ----------------------------------------------------------------------------------------------------------------------
* 配置集群的Tomcat实例的名称
* 这里jvmRoute属性值要与workers.properties中设置的节点名相同,该值将做为后缀添加在每个由该结点生成的jsessionid后面
* 而mod_jk正是根据jsessionid后面的后缀来确定一个请求应由哪一个结点来处理,这也是实现session_sticky的基本保证
* [root@CentOS64 app]# vi /app/tomcat1/conf/server.xml (为<Engine/>节点增加jvmRoute属性,属性值为tomcat1)
* [root@CentOS64 app]# vi /app/tomcat2/conf/server.xml (为<Engine/>节点增加jvmRoute属性,属性值为tomcat2)
* [root@CentOS64 app]# vi /app/tomcat3/conf/server.xml (为<Engine/>节点增加jvmRoute属性,属性值为tomcat3)
* ----------------------------------------------------------------------------------------------------------------------
* 配置集群参数
* 0)如果tomcat是放在不同机器上面的
* 那么直接取消注释tomcat/conf/server.xml中的<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>即可
* 1)如果tomcat是放在同一机器上面的(参考http://tomcat.apache.org/tomcat-6.0-doc/cluster-howto.html)
* 此时就要修改<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>
* 我们要做的就是显式添加此默认配置,并修改上面的Receiver port,由于我们这里有三个tomcat节点,故这个端口就依次修改为4001,4002,4003
* 之所以没有修改为4100,4200,4300,是由于Tomcat官方建议此端口范围在4000~4100之间
* ----------------------------------------------------------------------------------------------------------------------
* 应用程序为集群做的准备
* 1)Session中存放的数据必须实现序列化
* 2)在应用程序的web.xml中加入<distributable/>元素
* ----------------------------------------------------------------------------------------------------------------------
* 测试集群
* 启动apache和3个tomcat后,测试方法如下(测试代码已在下方列出)
* 1)访问测试页面,会看到页面打印SessionID后面多出了".tomcat2",这就是上文说到的jsessionid后面会加上.jvmRoute为后缀,表明tomcat2在处理此请求
* 2)刷新测试页面,会看到打印的SessionID没有变化(与负载均衡后的效果有明显不同,详见http://blog.csdn.net/jadyer/article/details/39617801)
* 3)添加新的属性,提交表单后会发现打印出了新添加的属性名和属性值,接着刷新页面会发现添加的属性依然存在
* 4)最关键的一步,关闭tomcat2服务器,再刷新页面,会发现请求交由"tomcat1"来处理了,并且之前添加的属性依然存在..至此,Session共享成功,集群成功
* ----------------------------------------------------------------------------------------------------------------------
* 注意事项
* 1)java.net.BindException: Cannot assign requested address; No faulty members identified
* 启动tomcat时报告上面的异常,猜测可能是由于tomcat安装在我的虚拟机中,该属性导致其与我的主机(Thinkpad笔记本)的IP产生冲突
* 此时修改[address="auto"]中auto为192.168.0.103(即tomcat服务器的IP)即可..如果还报这个异常,可以试一下127.0.0.1
* 2)如果仍然启动失败,或者启动成功,但无法实现session共享,那么有可能是组播出现了问题
* 因为tomcat中的集群原理是通过组播的方式进行节点的查找并使用TCP连接进行会话的复制的,即tomcat的session同步功能需要用到组播服务
* 可以通过[route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0]命令开通Linux组播服务
* 如果需要服务器启动时即开通组播,则需/etc/sysconfig/static-routes文件中加入[eht0 net 224.0.0.0 netmask 240.0.0.0]
* 另外,可以通过[netstat -g]或者[route -e]命令来查看组播状态
* ----------------------------------------------------------------------------------------------------------------------
* @create Sep 27, 2014 7:59:32 PM
* @author 玄玉<http://blog.csdn.net/jadyer>
*/
<%@ page language="java" pageEncoding="UTF-8"%>
<%
out.println("<br>Session ID : " + session.getId() + "<br>");
session.setAttribute("myname", "session");
String dataName = request.getParameter("dataName");
if(null!=dataName && dataName.length()>0){
String dataValue = request.getParameter("dataValue");
session.setAttribute(dataName, dataValue);
}
out.print("<b>Session 列表</b><br>");
java.util.Enumeration e = session.getAttributeNames();
while(e.hasMoreElements()){
String name = (String)e.nextElement();
String value = session.getAttribute(name).toString();
out.println( name + " = " + value+"<br>");
}
%>
<form action="demo.jsp" method="POST">
属性名:<input type=text size=20 name="dataName"><br>
属性值:<input type=text size=20 name="dataValue"><br>
<input type=submit>
</form>