【IT168技術文檔】Tomcat
5服務器為集群和SESSION復制提供了集成的支持。本系列的第一篇文章將為大家提供SESSION持久性以及TOMCAT集群中SESSION復制的
內在工作機制一個概要認識。我將會討論SESSION復制在TOMCAT5中是怎樣進行的以及跨越多集群節點的SESSION持久性的復制機制。在第2部
分,我會詳細討論一個帶有SESSION復制功能的TOMCAT集群的安裝例子,并且比較不同的復制情形。
集群 傳統獨立服務器(非集群的)不提供任何失效無縫轉移以及負載平衡能力。當服務器失敗的時候,就無法獲取整個網站的內容,除非服務器被重新喚起。由于服務器失效,任何存儲在服務器內存中的SESSION都會丟失,用戶必須重新登陸并且輸入所有由于服務器失效丟失的數據。
不同的是,作為集群一部分的服務器則提供了可測性以及失效無縫轉移能力。一個集群就是一組同步運行并且協同工作,能提供高可靠性,高穩定性以及高可測性
的多服務器例程。服務端集群對客戶端表現出來似乎就是一個單獨的服務器例程。從客戶端的視角來看,集群的客戶端和單獨的服務器沒多大不同,但是他們通過提
供實效無縫轉移和SESSION復制做到了不間斷服務以及SESSION數據持久性。
集群中的服務器通訊 集群中的應用程序服務器通過諸如IP多點傳送(IP multicast)和IP sockets這樣的技術和其他服務器共享信息
●IP多點傳送:主要用于1對多的服務器通訊,通過廣播服務和 heartbeats消息的可用來顯示服務器的有效
●IP sockets:主要用于在集群的服務器例程中進行P2P服務器通訊
使用IP多點傳送進行一對多通訊
TOMCAT服務器使用IP多點傳送在集群中的服務器例程間進行一對多的通訊,IP多點傳送是一種能夠讓多服務器向指定IP地址和端口號進行訂閱并且監
聽消息的廣播技術(多點傳送IP地址范圍從224.0.0.0 到239.255.255.255)。在集群中的每個服務器都使用多點傳送廣播特定的
heartbeat消息,通過監視這些
heartbeat消息,在集群中的服務器例程判斷什么時候服務器例程失效。在服務器通訊中使用IP多點傳送的一個缺點是他不能保證這些消息被確實接收到
了。例如,一個應用持續的本地多點傳送緩存滿了,就不能寫入新的多點傳送消息,等消息過了之后該應用程序就沒有被通知到。
使用IP Sockets進行服務器通訊
IP sockets 同樣也通過了一套在集群中的服務器間進行發送消息和數據的機制。服務器例程使用IP sockets
在集群節點間進行HTTP
SESSION狀態的復制。正確的SOKET配制對于集群的性能是至關重要的,基于SOCKET的通訊的效率取決于SOCKET的實現類別(例如:系統使
用本地的或者純JAVA SOCKET讀取器實現),如果服務器使用純JAVA
SOCKET讀取器則要看服務器例程是否注冊使用了足夠的SOCKET讀取器線程。
如果想要有最佳的SOCKET性能,系統應該
注冊使用本地的SOCEKT而不是純JAVA實現。這是因為相對于基于JAVA的SOCKET實現,本地SOCKET消耗更少的系統資源。雖然
SOCKET讀取器的JAVA實現是P2P通信中一種可靠而且可移動的方法,可是他不能為集群中的重型SOCKET使用提供最好的性能。當判斷從
SOCKET是否有數據讀取的時候本地SOCKET讀取器使用了更有效率的方法。使用本地SOCKET讀取器實現,讀取器線程不需要去統計靜止的
SOCKET:他們僅僅為活動的SOCKET服務,并且在一個給定的SOCKET開始活躍起來時他們可以立刻捕捉到。而使用純JAVA
SOCKET讀取器,線程必須動態的統計所有打開的SOCKET,判斷他們是否包含可讀取的數據。換句話說,SOCKET讀取器總是忙于統計
SOCKET,即使這些SOCKET沒有數據可讀。這些本不應該的系統開銷降低了性能。
TOMCAT 5中的集群
雖然在TOMCAT5的早些版本中也有集群的功能,但是在稍后的版本中(5。0。19或者更高),集群變的更加模塊組件化。在 server.xml
中集群元素已經被重構,這樣我們可以替換集群的不同部分而不會影響其他元素。例如,當前配置中把成員服務設置為多點傳送發現。這里可以輕易地把成員服務修
改替換為使用TCP或者 Unicast ,而不會改變集類邏輯的其他部分。
其他一些集群元素,例如SESSION管理器,復制發送端,復制接受端也可以被自定義的實現取代而不影響集群配置的其他部分。同樣,在TOMCAT集群中的任何服務器組件可以使用集類API向集群中的所有成員發送消息。
SESSION復制
服務器集群通常操縱兩種SESSION: sticky sessions和 replicated sessions 。sticky
sessions就是存在單機服務器中的接受網絡請求的SESSION,其他集群成員對該服務器的SESSION狀態完全不清楚,如果存有SESSION
的服務器失敗的話,用戶必須再次登陸網站,重新輸入所有存儲在SESSION中的數據。
另一種SESSION類型是,在一臺服務
器中SESSION狀態被復制到集群中的其他所有服務器上,無論何時,只要SESSION 被改變,SESSION數據都要重新被復制。這就是
replicated session 。 sticky 和 replicated sessions都有他們的優缺點, Sticky
sessions簡單而又容易操作,因為我們不必復制任何SESSION數據到其他服務器上。這樣就會減少系統消耗,提高性能。但是如果服務器失敗,所有
存儲在該服務器內存中的SESSION數據也同樣會消失。如果SESSION數據沒有被復制到其他服務器,這些SESSION就完全丟失了。當我們在進行
一個查詢事務當中的時候,丟失所有已經輸入的數據,就會導致很多問題。
為了支持 JSP HTTP session 狀態的自動失效無縫轉移,TOMCAT服務器復制了在內存中的SESSION狀態。這是通過復制存儲在一臺服務器上的SESSION數據到集群中其他成員上防止數據丟失以及允許失效無縫轉移。
對象的狀態管理 通過在服務器上的保存狀態可以區分出4種對象:
●無狀態:一個無狀態對象在調用的時候不會在內存中保存任何狀態,因為客戶端和服務器端沒必要保存任何有關對方的信息。在這種情況下,客戶端會在每次請
求服務器時都會發送數據給服務器。SESSION狀態被在客戶端和服務器端來回發送。這種方法不總是可行和理想的,特別是當傳輸的數據比較大或者一些安全
信息我們不想保存在客戶端的時候;
●會話:一個會話對象在一個SESSION中只被用于特定的某個客戶端。在SESSION中,
他可以為所有來自該客戶端的請求服務,并且僅僅是這個客戶端的請求。貫穿一個SESSION,兩個請求間的狀態信息必須保存。會話服務通常在內存中保存短
暫的狀態,當在服務器失敗的時候可能會丟失。SESSION狀態通常被保存在請求間的服務器的內存中。為了清空內存,SESSION狀態也可以被從內存中
釋放(就像在一個對象CACHE)。在該對象中,性能和可量測性都有待提高,因為更新并不是被單獨的寫到磁盤上,并且服務器失敗的時候數據也沒辦法搶救。
●緩存:緩存對象在內存中保存狀態,并且使用這個去處理從多客戶端來的請求。緩存服務的實現可以擴展到他們把緩存的是數據備份保存在后端存儲器中(通常是一個關系數據庫)。
●獨立的:一個獨立的對象在一個時間內只活躍在集群中的一臺服務器上,處理來自多客戶端的請求。他通常由那些私有的,持久的,在內存中緩寸的數據支持。
他同樣也在內存中保持短暫狀態,在服務器失敗的時候要重建或者丟失。當失敗的時候,獨立對象必須在同一個服務器上重起或者移植到另一臺服務器上。
(來源: "Using WebLogic Server Clusters")
SESSION復制的設計考慮事項
網絡考慮事項
把集群的多點傳送地址和其他應用程序隔離是至關重要的。我們不希望集群配置或者網絡布局干擾到多點傳送服務器通信。和其他應用程序共享集群多點傳送地址
將迫使集群的服務器例程處理不應該的消息,消耗系統內存。共享多點傳送地址可能也會使IP多點傳送緩沖過載,延遲服務器 heartbeat
消息傳輸。這樣的延遲可能導致一個服務器例程被標識為死亡,僅僅因為他的 heartbeat 消息沒有被及時接收。
編程考慮事項 除了上面提到的網絡相關因素,還有些和我們寫 J2EE 網絡應用程序有關的設計考慮也會影響SESSION復制。以下列出了一些編程方面的考慮:
●SESSION數據必須被序列化:為了支持HTTP session 狀態的內存內復制,所有的 servlet 和 JSP session 數據必須被序列化,對象中的每個域都必須被序列化,這樣對象被可靠的序列化。
●把應用程序設計為冪等的:冪等的的意思就是一個操做不會修改狀態信息,并且每次操作的時候都返回同樣的結果(換句話說就是:做多次和做一次的效果是一
樣的),通常,WEB請求,特別是 HTML forms
都被發送多次(當用戶點擊發送按紐兩次,重載頁面多次),導致多次HTTP請求。設計SERVLET和其他WEB對象為
冪等的,可以容忍多次請求。詳細可以去參考設計模式“Synchronized Token ”和“Idempotent Receiver
”關于怎樣設計冪等的的應用程序。
●在BUSINESS層存儲狀態:會話狀態應該使用有狀態的SESSION
BEANS存儲在EJB層,而不是存儲在WEB層的HttpSession。因為企業應用程序要支持各種類型客戶端(WEB客戶端,JAVA應用程序,其
他EJB),存儲數據在WEB層會導致在客戶端的雙數據存儲。因此,有狀態的SESSION
BEAN在這些情況下就被用于存儲SESSION狀態。無狀態的SESSION
BEAN要為每次的調用重構造會話狀態。這些狀態可能必須從數據庫中恢復的數據中重編譯。這些缺點失去了使用無狀態SESSION
BEAN去提高性能和可測量性的目的,嚴重的減低了性能。
●序列化系統消耗:序列化SESSION數據在復制SESSION狀態的時候回會些系統消耗。隨著序列化對象大小的增長消耗也越多。最好是保持SE
實踐中整理出tomcat集群和負載均衡
(一)環境說明
(1)服務器有4臺,一臺安裝apache,三臺安裝tomcat
(2)apache2.0.55、tomcat5.5.15、jk2.0.4、jdk1.5.6或jdk1.4.2
(3)ip配置,一臺安裝apache的ip為192.168.0.88,三臺安裝tomcat的服務器ip分別為192.168.0.1/2/4
(二)安裝過程
(1)在三臺要安裝tomcat的服務器上先安裝jdk
(2)配置jdk的安裝路徑,在環境變量path中加入jdk的bin路徑,新建環境變量JAVA_HOME指向jdk的安裝路徑
(3)在三臺要安裝tomcat的服務器上分別安裝tomcat,調試三個tomcat到能夠正常啟動
(4)tomcat的默認WEB服務端口是8080,默認的模式是單獨服務,我的三個tomcat的WEB服務端口修改為7080/8888/9999
修改位置為tomcat的安裝目錄下的conf/server.xml
修改前的配置為
<Connector port="8080" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" />
修改后的配置為
<Connector port="7080" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" />
依次修改每個tomcat的監聽端口(7080/8888/9999)
(5)分別測試每個tomcat的啟動是否正常
http://192.168.0.1:7080
http://192.168.0.2:8888
http://192.168.0.4:9999
(三)負載均衡配置過程
(1)在那臺要安裝apache的服務器上安裝apache2.0.55,我的安裝路徑為默認C:\Program Files\Apache Group\Apache2
(2)安裝后測試apache能否正常啟動,調試到能夠正常啟動http://192.168.0.88
(3)下載jk2.0.4后解壓縮文件
(4)將解壓縮后的目錄中的modules目錄中的mod_jk2.so文件復制到apache的安裝目錄下的modules目錄中,我的為C:\Program Files\Apache Group\Apache2\modules
(5)修改apache的安裝目錄中的conf目錄的配置文件httpd.conf,在文件中加LoadModule模塊配置信息的最后加上一句LoadModule jk2_module modules/mod_jk2.so
(6)分別修改三個tomcat的配置文件conf/server.xml,修改內容如下
修改前
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host). -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Standalone" defaultHost="localhost" jvmRoute="jvm1">
-->
<!-- Define the top level container in our container hierarchy -->
<Engine name="Catalina" defaultHost="localhost">
修改后
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
analyzes the HTTP headers included with the request, and passes them
on to the appropriate Host (virtual host). -->
<!-- You should set jvmRoute to support load-balancing via AJP ie :-->
<Engine name="Standalone" defaultHost="localhost" jvmRoute="tomcat1">
<!-- Define the top level container in our container hierarchy
<Engine name="Catalina" defaultHost="localhost">
-->
將其中的jvmRoute="jvm1"分別修改為jvmRoute="tomcat1"和jvmRoute="tomcat2"和jvmRoute="tomcat3"
(7)然后重啟三個tomcat,調試能夠正常啟動。
(8)在apache的安裝目錄中的conf目錄下創建文件workers2.propertie,寫入文件內容如下
# fine the communication channel
[channel.socket:192.168.0.1:8009]
info=Ajp13 forwarding over socket
#配置第一個服務器
tomcatId=tomcat1 #要和tomcat的配置文件server.xml中的jvmRoute="tomcat1"名稱一致
debug=0
lb_factor=1 #負載平衡因子,數字越大請求被分配的幾率越高
# Define the communication channel
[channel.socket:192.168.0.2:8009]
info=Ajp13 forwarding over socket
tomcatId=tomcat2
debug=0
lb_factor=1
# Define the communication channel
[channel.socket:192.168.0.4:8009]
info=Ajp13 forwarding over socket
tomcatId=tomcat3
debug=0
lb_factor=1
[status:]
info=Status worker, displays runtime information.
[uri:/jkstatus.jsp]
info=Display status information and checks the config file for changes.
group=status:
[uri:/*]
info=Map the whole webapp
debug=0
(9)在三個tomcat的安裝目錄中的webapps建立相同的應用,我和應用目錄名為TomcatDemo,在三個應用目錄中建立相同 WEB-INF目錄和頁面index.jsp,index.jsp的頁面內容如下
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.util.*" %>
<html><head><title>Cluster App Test</title></head>
<body>
Server Info:
<%
out.println(request.getLocalAddr() + " : " + request.getLocalPort()+"<br>");%>
<%
out.println("<br> 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("<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>
(10)重啟apache服務器和三個tomcat服務器,到此負載 均衡已配置完成。測試負載均衡先測試apache,訪問http://192.168.0.88/jkstatus.jsp
能否正常訪問,并查詢其中的內容,有三個tomcat的相關配置信息和負載說明,訪問http://192.168.0.88/TomcatDemo/index.jsp看能夠運行,
能運行,則已建立負載均衡。
(四)tomcat集群配置
(1)負載均衡配置的條件下配置tomcat集群
(2)分別修改三個tomcat的配置文件conf/server.xml,修改內容如下
修改前
<!--
<Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
managerClassName="org.apache.catalina.cluster.session.DeltaManager"
expireSessionsOnShutdown="false"
useDirtyFlag="true"
notifyListenersOnReplication="true">
<Membership
className="org.apache.catalina.cluster.mcast.McastService"
mcastAddr="228.0.0.4"
mcastPort="45564"
mcastFrequency="500"
mcastDropTime="3000"/>
<Receiver
className="org.apache.catalina.cluster.tcp.ReplicationListener"
tcpListenAddress="auto"
tcpListenPort="4001"
tcpSelectorTimeout="100"
tcpThreadCount="6"/>
<Sender
className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
replicationMode="pooled"
ackTimeout="5000"/>
<Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
<Deployer className="org.apache.catalina.cluster.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.cluster.session.ClusterSessionListener"/>
</Cluster>
-->
修改后
<!-- modify by whh -->
<Cluster className="org.apache.catalina.cluster.tcp.SimpleTcpCluster"
managerClassName="org.apache.catalina.cluster.session.DeltaManager"
expireSessionsOnShutdown="false"
useDirtyFlag="true"
notifyListenersOnReplication="true">
<Membership
className="org.apache.catalina.cluster.mcast.McastService"
mcastAddr="228.0.0.4"
mcastPort="45564"
mcastFrequency="500"
mcastDropTime="3000"/>
<Receiver
className="org.apache.catalina.cluster.tcp.ReplicationListener"
tcpListenAddress="auto"
tcpListenPort="4001"
tcpSelectorTimeout="100"
tcpThreadCount="6"/>
<Sender
className="org.apache.catalina.cluster.tcp.ReplicationTransmitter"
replicationMode="pooled"
ackTimeout="5000"/>
<Valve className="org.apache.catalina.cluster.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
<Deployer className="org.apache.catalina.cluster.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.cluster.session.ClusterSessionListener"/>
</Cluster>
<!-- modify by whh -->
將集群配置選項的注釋放開即可,如上。
(3)重啟三個tomcat。到此tomcat的集群已配置完成。
(五)應用配置
對于要進行負載和集群的的tomcat目錄下的webapps中的應用中的WEB-INF中的web.xml文件要添加如下一句配置
<distributable/>
配置前
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<display-name>TomcatDemo</display-name>
</web-app>
配置后
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<display-name>TomcatDemo</display-name>
<distributable/>
</web-app>
問:tomcat集群是怎么處理session的阿
答:在tomcat做集群之后,每個tomcat之間自動根據tomcat的配置文件中的參數進行session復制,
對于一個客戶端對說,只要是同一個IP,那它每次上傳的sessionID就是一樣的,