Tomcat 5集群中的SESSION復制 第一部分

作者: Srini Penchikala 11/24/2004

翻譯:Sunny983


版權聲明:可以任意轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
作者:
Srini Penchikala;Sunny983
原文地址:http://www.onjava.com/pub/a/onjava/2004/11/24/replication1.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43865_Tomcat_Clustering.html
關鍵詞: Tomcat Clustering

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狀態的時候回會些系統消耗。隨著序列化對象大小的增長消耗也越多。最好是保持SESSION容量適當的小。但是如果你必須在SESSION中創建非常大的對象,最好測試下你的 servlets 性能以保證性能是可接受的以及SESSION的復制時間是適當的。
●用戶SESSION:判斷在集群中每個TOMCAT服務器例程所控制的并發用戶SESSION最大數是很重要的。為了控制更多并發SESSION,我們應該為提高效率添加更多內存。最大并發客戶端數,以及每個客戶端請求的頻率在決定SESSION復制對服務器的性能影響方面也是個因素。

Tomcat 5中的SESSION復制
在版本5之前,TOMCAT服務器只支持sticky sessions (使用mod_jk模塊進行負載平衡)。如果我們需要SESSION復制,必須依靠第3方軟件例如JavaGroups 去實現。 Tomcat 5服務器帶有SESSION復制功能。和集群特征類似,只要修改 server.xml 注冊文件就能實現SESSION復制。
Martin Fowler 在他的書《 Enterprise Patterns》中談到三個SESSION狀態持久性模式,這些模式包括:
1.客戶端SESSION狀態:在客戶端存儲SESSION狀態
2.服務端SESSION狀態:在一個序列化的FORM中保持SESSION狀態到一個服務器系統上。
3.數據庫SESSION狀態:當在數據庫中提交數據的時候存儲SESSION數據。

TOMCAT支持以下三種SESSION持久性類型:
1.內存復制:在JVM內存中復制SESSION狀態,使用TOMCAT 5安裝帶的SimpleTcpCluster 和 SimpleTcpClusterManager 類。這些類在包org.apache.catalina.cluster中,是server/lib/catalina-cluster.jar的一部分。
2.數據庫持久性:在這種類型中,SESSION狀態保存在一個關系數據庫中,服務器使用JDBCManager類從數據庫中獲取SESSION信息。這個類在包org.apache.catalina.session.JDBCStore中,是catalina.jar的一部分。
3.基于文件的持久性:這里使用類PersistenceManager把SESSION狀態保存到一個文件系統。這個類在包org.apache.catalina.session.FileStore中,是catalina.jar的一部分。

TOMCAT集群元素以及SESSION復制
這章簡要介紹一下組成TOMCAT集群的元素以及SESSION復制。

集群
這個是集群中的主要元素, SimpleTcpCluster類代表這個元素。他使用在server.xml中指定的管理類為所有可分配的web contexts生成ClusterManager。

集群管理器
這個類關注跨越集群中所有節點間的SESSION數據復制。所有在web.xml文件中指定了distributable標記的WEB應用程序都會有SESSION復制。集群管理器作為集群元素的managerClassName屬性在server.xml中指定。集群管理器的代碼主要被設計用來分離集群中的元素。我們所要做的就是寫一個SESSION管理器類去實現ClusterManager接口。這樣能讓我們靈活的使用客戶集群管理器而不會影響到集群中的其他元素。這里有兩個復制算法。SimpleTcpReplicationManager每次復制全部的SESSION,而DeltaManager只復制SESSION增量。

最簡單的復制管理器在每次HTTP請求都復制所有的SESSION。在SESSION較小的時候這樣是很有用的,我們可以只用以下代碼:

HashMap map = session.getAttribute("map");
map.put("data","data");


這里,我們不需要特別調用session.setAttribute() 或者 removeAttribute方法去復制SESSION變化。對于每次HTTP請求,在SESSION中的所有屬性都被復制。可以使用一個叫做useDirtyFlag的屬性去最優化SESSION被復制的次數。如果這個標記被設為真,我們必須調用setAttribute()方法去獲取被復制的SESSION變化。如果被設為假,每次請求后SESSION都被復制。

SimpleTcpReplicationManager生成ReplicatedSession執行SESSION復制工作。

提供增量管理器僅僅是為了性能的考慮。它在每次請求的時候都做一次復制。同時他也調用監聽器,所以如果我們調用session.setAttribute(),那么在其他服務器上的監聽器就會被調用。DeltaManager生成DeltaSession執行 SESSION復制。

成員
成員的建立通過由TOMCAT例程在同樣的多點傳送IP和端口發送廣播消息。廣播的消息包括服務器的IP地址和TCP監聽端口(默認IP地址為228.0.0.4)。
如果在給定的時間框架內一個例程沒有接收到消息(由集群配置中的mcastDropTime參數指定),成員被認為死亡。這個元素由McastService類表示。
由mcastXXX開始的屬性用于成員資格多點傳送PING。以下表格列出了用于IP多點傳送服務器通訊的屬性。

image

發送端
這個元素由ReplicationTransmitter類代表。當多點傳送廣播消息被接收到,成員被添加到機群。在下次復制請求前,發送例程將使用主機和端口信息建立一個TCP SOCKET。使用這些SOCKET發送序列化的數據。在TOMCAT 5中有3種不同的方法操縱SESSION復制:異步,同步,池復制模式。以下部分解釋了這些模式怎樣工作,以及他們將被使用在什么情況下。
●異步:在這種復制模式中,每個集群節點都有一個單線程扮演SESSION數據傳送器。這里,請求線程會把復制請求發送到一個隊列,然后返回給客戶端。在失效無縫轉移前,如果我們擁有sticky sessions,就應該使用異步復制。這里復制時間不是至關重要的,但是請求時間卻是。在異步復制期間,請求在數據被復制完之前就返回了。這種復制模式縮短了請求時間。當每個請求被分的更開這種模式是很有用的。(例如,在WEB請求間有更長的延遲)。同樣,如果我們不關心SESSION是否完成復制這個也很有用,當SESSION很較小時, SESSION復制時間也更短。
●同步:在這種模式中,一個單線程執行了HTTP請求和數據復制。集群中所有節點都接收到SESSION數據后線程才返回。同步意味被被復制的數據通過一個單SOCKET發送。由于使用一個單線程,同步模式可能會是簇性能的一個潛在的瓶頸。這種復制模式保證了在請求返回前SESSION        已經被復制。
●池:TOMCAT5 在使用池復制模式進行SESSION復制的方法上提供了很大的改進。池模式基本上是同步模式的一個擴展版本。他基于的一個原則是:劃分服務到多例程,每個例程處理不同的SESSION數據片段。同時對接收服務器開放多SOCKET進行發送SESSION信息。這種方法比通過單SOCKET發送所有東西更快。因此,使用一個SOCKET池同步復制SESSION。直到所有SESSION數據都復制完請求才返回。為了有效使用這種模式要增加TCP線程。由于使用多SOCKET,池模式使得集群性能的逐步提高以及更好的可測性。同樣這種模式也是最安全的配置,因為它有足夠數量的SOCKET在理想的時間內發送所有的SESSION數據到其他節點上。而使用單SOCKET,SESSION數據在通過集群時可能丟失或者只是部分傳輸。

接收端
這個集群元素由類ReplicationListener表示。在集群配置中以tcpXXX開始的屬性用于TCP SESSION復制。以下表格列出了用于配置服務器復制中基于SOCEKT服務器通訊的屬性。

image

復制值
復制值用于判斷哪些HTTP請求需要被復制。由于我們不經常復制靜態內容(例如HTML和JavaScript, stylesheets,圖像文件),我們可以使用復制值元素過濾掉靜態內容。這個值可以用于找出什么時候請求已完成以及初始化復制。

部署器
部署器元素可以用于部署集群范圍的應用程序。通常,部署只部署/解除部署簇內的工作成員。所以在損壞的節點在啟動時沒有WARS的復制。當watchEnabled="true"時配置器為WAR文件監視一個目錄(watchDir)。當添加一個新的WAR文件時,WAR被部署到本地例程,然后被部署到集群中的其他例程。當一個WAR文件從watchDir刪除,這個WAR被從本地和集群范圍內解除部署。
所有在TOMCAT集群結構中的元素以及他們的層次關系都在列在圖1中

?????
圖 1. Tomcat 集群等級結構圖。單擊看原圖。

TOMCAT中SESSION復制是怎么工作的
以下部分簡要解釋當TOMCAT服務器啟動或則關閉時集群節點怎樣分享SESSION信息,詳細信息可參考Tomcat 5 Clustering文擋。

TC-01:集群中第一個節點
TC-02:集群中第2個節點

●服務器啟動:TC-01使用標準服務器啟動隊列啟動。當主機對象被創建,即有一個集群對象和它相關聯。當contexts被解析,如果distributable已經在web.xml中指定,那么TOMCAT為WEB CONTEXT創建SESSION管理器(SimpleTcpReplicationManager 取代StandardManager)。集群將會啟動一個成員服務(成員的一個例程)和一個復制服務。
當TC-02啟動,他也遵循第一個成員(TC-01)同樣的隊列但是有一個不同。集群被啟動并且創建一個成員關系(TC-01,TC-02)。TC-02將向TC-01請求SESSION狀態。TC-01回應該請求,在TC-2開始監聽HTTP請求前,TC-01發送狀態給TC-02。如果TC-01不回應,TC-02將在60秒后進入中止狀態并且發布一個日志入口。SESSIONG 狀態發送給所有在web.xml中指定了distributable的WEB應用程序。
●創建SESSION:當TC-01接收到請求,一個SESSION(S1)被創建,處理進入TC-01的請求和沒有SESSION復制時是一樣的。當請求完成時會有以下事件:ReplicationValve將會在回應返回給用戶前截取請求。這里,會發現SESSION已經被改變,使用TCP復制SESSION到TC-02。
●服務器儲運損耗/關閉:當在集群中的一臺服務器失敗,維護損壞或者系統升級,其他節點會受到第一個節點已經脫離集群的通知。TC-02從他的成員資格列刪除TC-01,并且TC-02在也不會收到有關TC-01任何變動的通知。負載平衡將會移至TC-02,所有的SESSION由TC-02控制。
當TC-01開始恢復,他再次遵循在服務器開始階段描述的啟動隊列。加入到簇中并且以所有SESSIONG的當前狀態和TC-02通訊。一旦接收到        SESSIONG狀態,也就完成了加載然后打開它的HTTP/ mod_jk端口。所以,要等到從TC-2接受到SESSION狀態TC-01才能發送請求。
●SESSION終止:如果在第一個節點的一個SESSION已經無效或則由于過期終止,無效請求將被截取,SESSION會被同其他無效SESSION放在一個隊列中。當請求完成,服務器發送SESSIONG終止消息給TC-02而不是發送已經改變的SESSION,TC-02同樣也會把該SESSION置無效。我們可以從服務器控制臺看到SESSIONG無效的消息。無效SESSION在集群中將不會被復制,直到其他請求傳出系統并且檢查無效隊列。

結束語
在這篇文章中,我談了有關在集群環境中的SESSION復制,以及編寫要求在內存內SESSIONG復制的J2EE應用程序時的一些設計注意事項。我同時也討論了在TOMCAT 5容器中被指定來進行SESSIONG復制的簇元素。在這個系列的第2部分,我們將會看看怎樣使用不同的SESSIONG 管理器和復制模式在TOMCAT集群中配置SESSIONG復制。

Srini Penchikala 是一個在Flagstar 銀行工作的信息系統主題專家。