詳解Tomcat集群如何同步會話
Tocmat集群中最重要的交換信息就是會話消息,對某個tomcat實例某會話做的更改要同步到集群其他tomcat實例的該會話對象,這樣才能保證集群所有實例的會話數(shù)據(jù)一致。在tribes組件的基礎(chǔ)上完成這些工作就相當(dāng)容易些,tribes是tomcat實現(xiàn)的一個通信框架。
如下圖,tomcat實現(xiàn)會話同步的過程中大致會使用如下組件,現(xiàn)在假設(shè)中間的tomcat實例的會話改變了,它會通過會話管理器Manager將改變的動作消息封裝成消息然后調(diào)用集群對象Cluster,通過Cluster將消息發(fā)送出去,同時Cluster又依賴于tribes,最后消息其實是交由tribes真正發(fā)送的,通信過程是以ClusterMessage為對象傳輸?shù)?,它會先被序列化進行傳輸,到達左邊和右邊的tomcat實例時會被反序列化,消息由tribes接收后往Cluster上傳,最后到達會話管理器Manager,Manager根據(jù)動作消息去同步會話。

所以Cluster其實就是實現(xiàn)了ChannelListener的監(jiān)聽類,當(dāng)tribes接收到消息后就會調(diào)用此監(jiān)聽器的messageReceived方法處理邏輯,此方法又會繼續(xù)往上通知Manager的messageDataReceived方法,此方法內(nèi)完成會話同步處理邏輯。關(guān)于會話具體的同步機制tomcat提供了兩種,分別是“集群增量會話管理器——DeltaManager”和“集群備份會話管理器——BackupManager”。
DeltaManager會話管理器
DeltaManager會話管理器是tomcat默認的集群會話管理器,它主要用于集群中各個節(jié)點之間會話狀態(tài)的同步維護。集群增量會話管理器的職責(zé)是將某節(jié)點的會話該變同步到集群內(nèi)其他成員節(jié)點上,它屬于全節(jié)點復(fù)制模式,所謂全節(jié)點復(fù)制是指集群中某個節(jié)點的狀態(tài)變化后需要同步到集群中剩余的節(jié)點,非全節(jié)點方式可能只是同步到其中某個或若干節(jié)點。
在集群中全節(jié)點會話復(fù)制的一個大致步驟如下圖所示,客戶端發(fā)起一個請求,假設(shè)通過一定的負載均衡設(shè)備分發(fā)策略分到其中一個結(jié)點node1,如果還未存在session對象的話web容器將會創(chuàng)建一個會話對象,接著執(zhí)行一些邏輯處理,在對客戶端響應(yīng)之前有個重要的事情是要把session對象同步到集群中其他節(jié)點上,最后再響應(yīng)客戶端。當(dāng)客戶端第二次發(fā)起請求時,假如分發(fā)到node3節(jié)點上,由于同步了node1的session會話,所以在執(zhí)行邏輯時并不會取不到session的值。如果刪除某個會話對象則要同時通知其他節(jié)點把相應(yīng)會話刪除,如果修改了某個會話的某些屬性也同樣要更新到其他節(jié)點的會話中。

DeltaManager其實就是一個會話同步通信解決方案,除了具備上面提到的全節(jié)點復(fù)制外,它還有具有只復(fù)制會話增量的特性,增量是以一個完整請求為周期,即會將一個請求過程中所有會話修改量在響應(yīng)前進行集群同步。往下看Tomcat具體實現(xiàn)方案。
為區(qū)分不同的動作必須要先定義好各種事件,例如會話創(chuàng)建事件、會話訪問事件、會話失效事件、獲取所有會話事件、會話增量事件、會話ID改變事件等等,實際上tomcat集群會有9種事件,集群根據(jù)這些不同的事件就可以彼此進行通信,接收方對不同事件做不同的操作。
如下圖,例如node1節(jié)點創(chuàng)建完一個會話后,即向其他三個節(jié)點發(fā)送EVT_SESSION_CREATED事件,其他三個節(jié)點接收到此事件后則各自在自己本地創(chuàng)建一個會話,會話包含了兩個很重要的屬性——會話ID和創(chuàng)建時間,這兩個屬性都必須由node1節(jié)點跟著EVT_SESSION_CREATED一起發(fā)送出去,本地會話創(chuàng)建成功后即完成了會話創(chuàng)建同步工作,此時你通過會話ID查找集群中任意一個節(jié)點都可以找到對應(yīng)的會話。同樣對于會話訪問事件,node1向其他節(jié)點發(fā)送EVT_SESSION_ACCESSED事件及會話ID,其他節(jié)點根據(jù)會話ID找到對應(yīng)會話并更新會話最后訪問時間,以免被認為是過期會話而被清理。類似的還有會話失效事件(同步集群銷毀某會話)、會話ID改變事件(同步集群更改會話ID)等等操作。

Tomcat使用SessionMessageImpl類定義了各種集群通信事件及操作方法,在整個集群通信過程中就是按照此類定義好的事件進行通信,SessionMessageImpl包含的事件如下{ EVT_SESSION_CREATED、EVT_SESSION_EXPIRED、EVT_SESSION_ACCESSED、EVT_GET_ALL_SESSIONS、EVT_SESSION_DELTA、EVT_ALL_SESSION_DATA、EVT_ALL_SESSION_TRANSFERCOMPLETE、EVT_CHANGE_SESSION_ID、EVT_ALL_SESSION_NOCONTEXTMANAGER },除此之外它繼承了序列化接口(方便序列化)、集群消息接口(集群的操作)、會話消息接口(事件定義及會話操作)。

集群增量會話管理器DeltaManager可以說是通過SessionMessageImpl消息來管理DeltaSession,即根據(jù)SessionMessageImpl里面的事件響應(yīng)不同的操作。Tomcat的集群通信使用的是tribes組件(相關(guān)章節(jié)會對tribes組件詳細分析),網(wǎng)絡(luò)IO都交由tribes后應(yīng)用可以更專注邏輯處理,DeltaManager存在一個messageDataReceived(ClusterMessage cmsg)方法,此方法會在本節(jié)點接收到其他節(jié)點發(fā)送過來的消息后被調(diào)用,且傳入的參數(shù)為ClusterMessage類型,可轉(zhuǎn)化為SessionMessage類型,然后根據(jù)SessionMessage定義的9種事件做不同處理。
其中有一個事件需要關(guān)注的是EVT_SESSION_DELTA,它是對會話增量同步處理的事件,某個節(jié)點在一個完整的請求過程中對某會話相關(guān)屬性的所有操作被抽象到了DeltaRequest對象中,而DeltaRequest被序列化后會放到SessionMessage中,所以EVT_SESSION_DELTA事件處理邏輯就是從SessionMessage獲取并反序列化出DeltaRequest對象,再將DeltaRequest包含的對某個會話的所有操作同步到本地該會話中,至此完成會話增量同步。

總的來說DeltaManager就是DeltaSession的管理器,它提供了會話增量的同步方式而不是全量同步,極大提高了同步效率。
集群備份會話管理器——BackupManager
全節(jié)點復(fù)制的網(wǎng)絡(luò)流量隨節(jié)點數(shù)量增加呈平方趨勢增長,也正是因為這個因素導(dǎo)致無法構(gòu)建較大規(guī)模的集群,為了使集群節(jié)點能更加大,首要解決的就是數(shù)據(jù)復(fù)制時流量增長的問題,于是tomcat提出了另外一種會話管理方式,每個會話只會有一個備份,它使會話備份的網(wǎng)絡(luò)流量隨節(jié)點數(shù)量的增加呈線性趨勢增長,大大減少了網(wǎng)絡(luò)流量和邏輯操作,可構(gòu)建較大的集群。
下面看看這種方式具體的工作機制,集群一般是通過負載均衡對外提供整體服務(wù),所有節(jié)點被隱藏在后端組成一個整體。前面各種模式的實現(xiàn)都無需負載均衡協(xié)助,所以圖中都把負載均衡省略了。最常見的負載方式是前面用apache拖所有節(jié)點,它支持將類似“326257DA6DB76F8D2E38F2C4540D1DEA.tomcat1”的會話id進行分解,定位到tomcat集群中以tomcat1命名的節(jié)點上(這種方式稱為Session Stick,由apache jk模塊實現(xiàn))。
每個會話存在一個原件和一個備份,且備份與原件不會保存在同一個節(jié)點上,如下圖,例如當(dāng)客戶端發(fā)起請求后通過負載均衡被分發(fā)到tomcat1實例節(jié)點上,生成一個包含.tomcat1后綴的會話標(biāo)識,并且tomcat1節(jié)點根據(jù)一定策略選出此次會話對象備份的節(jié)點,然后將包含了{會話id,備份ip}的信息發(fā)送給tomcat2、tomcat3、tomcat4,如圖中虛線所示,這樣每個節(jié)點都有一個會話id、備份ip列表,即每個節(jié)點都有每個會話的備份ip地址。
完成上面一步后就是將會話內(nèi)容備份到備份節(jié)點上,假如tomcat1的s1、s2兩個會話的備份地址為tomcat2,則把會話對象備份到tomcat2中,類似的有tomcat2把s3會話備份到tomcat4,tomcat4把s4、s5兩個對話備份到tomcat3,這樣集群中所有的會話都已經(jīng)有了一份備份。當(dāng)tomcat1一直不出故障,由于Session Stick技術(shù)客戶端將一直訪問到tomcat1節(jié)點上,保證一直能獲取到會話。而當(dāng)tomcat1出故障了,這時tomcat也提供了一個failover機制,apache感知到后端集群tomcat1節(jié)點被移除了,這時它會把請求隨機分配到其他任意節(jié)點上,接下去會有兩種情況:
①剛好分到了備份節(jié)點tomcat2上,此時仍能獲取到s1會話,除此之外,tomcat2還要另外做的事是將這個s1會話標(biāo)記為原件且繼續(xù)選取一個備份地址備份s1會話,這樣一來又有了備份。
②假如分到了非備份節(jié)點tomcat3,此時肯定找不到s1會話,于是它將向集群所有節(jié)點發(fā)問,“請問誰有s1會話的備份ip地址信息?”,因為只有tomcat2有s1的備份地址信息,它接收到詢問后應(yīng)答告知tomcat3節(jié)點s1會話的備份在tomcat2,根據(jù)這個信息就能查到s1會話了,并且tomcat3在自己本地生成s1會話并標(biāo)為原件,tomcat2上的副本不變,這樣一來同樣能找到s1會話,正常完整整個請求處理。

接著分析Tomcat對上面機制詳細的實現(xiàn),正常情況下為了支持高效的并發(fā)操作,tomcat的所有會話集使用ConcurrentHashMap
public Object put(Object key, Object value) {
①實例化MapEntry,將key和value傳入,并設(shè)置源節(jié)點為目前節(jié)點。
②判斷本地內(nèi)存是否已包含key,如是則不僅要本地remove掉,還要跨節(jié)點remove。
③通過Round robin算法從MapMember中選擇一個作為備份節(jié)點。
④實例化一個包含MSG_BACKUP標(biāo)識的MapMessage對象并發(fā)送給備份節(jié)點。
⑤實例化一個包含MSG_PROXY標(biāo)識的MapMessage對象并發(fā)送給除了備份節(jié)點外的其他(代理)節(jié)點。
⑥put進本地緩存。
}
其次,再看看它如何通過get實現(xiàn)獲取會話對象操作:
public Object get(Object key) {
①獲取本地的MapEntry對象,它或許直接包含了會話對象,或許包含了會話對象的存放位置信息。
②判斷本節(jié)點是否屬于源節(jié)點,如為源節(jié)點則直接獲取MapEntry對象里面的會話對象并返回。
③判斷本節(jié)點是否屬于備份節(jié)點,若為備份節(jié)點則直接獲取MapEntry對象里面的會話對象作為返回對象,并且還要將本節(jié)點升為源節(jié)點、重新選取一個新備份節(jié)點,把MapEntry對象拷貝到新備份節(jié)點。
④判斷本節(jié)點是否屬于代理節(jié)點,若為代理節(jié)點則向其他節(jié)點發(fā)送會話對象拷貝請求,“集群中誰有此會話對象請發(fā)送給我”,把接收到的會話對象放到本節(jié)點并作為返回對象,最后將本節(jié)點升為源節(jié)點。
}
最后,看看刪除會話對象remove操作的實現(xiàn):
public Object remove(Object key) {
①刪除本地此MapEntry對象。
②廣播其他節(jié)點刪除此MapEntry對象。
}
通過上面三個方法已經(jīng)很清晰描述了新的Map是如何進行跨節(jié)點的增刪改查的,BackupManager會話管理器就是通過這個新的Map進行會話管理。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Linux上tomcat的虛擬主機IP映射配置(圖片服務(wù)器)
有時候我們會使用tomcat作為一個圖片資源服務(wù)器,本文主要介紹了Linux上tomcat的虛擬主機IP映射配置,通過tomcat服務(wù)器來訪問我們的圖片,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
Apache結(jié)合Tomcat實現(xiàn)動靜分離的方法
這篇文章主要介紹了Apache結(jié)合Tomcat實現(xiàn)動靜分離的方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01

