完整版見
https://jadyer.github.io/2014/09/28/tomcat-apache-cluster/
/**
* 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>