官網(wǎng)配置:http://tomcat.apache.org/tomcat-6.0-doc/cluster-howto.html
本文為譯文,屬個(gè)人英語學(xué)習(xí)文,如誤導(dǎo)了你,先說聲抱歉。
- 前言
添加Cluster到<Engine>或者<Host>元素中
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
增加上面配置后會(huì)激活all-to-all 的會(huì)話復(fù)制(由DeltaManager實(shí)現(xiàn)會(huì)話復(fù)制)。
all-to-all 意思為所有會(huì)話都會(huì)復(fù)制到集群中的其他節(jié)點(diǎn)上。
此模式適用于小的集群模式,不適合大的集群模式(有許多的tomcat節(jié)點(diǎn))。
DeltaManager模式會(huì)將會(huì)話復(fù)制到所有節(jié)點(diǎn)上,即使這節(jié)點(diǎn)Tomcat并沒有部署應(yīng)用。為了避免這一問題,你可以使用BackupManager,BackupManager只會(huì)將會(huì)話復(fù)制到有部署應(yīng)用的節(jié)點(diǎn)上。但是BackupManager有一個(gè)缺點(diǎn),它的實(shí)用性沒有DeltaManager好。
重要默認(rèn)信息
1、組播地址是228.0.0.4
2、組播端口是45564(端口和地址組合確定集群成員)
3、IP地址獲取方式j(luò)ava.net.InetAddress.getLocalHost()
.getHostAddress()(確保IP非127.0.0.1,這是一種常見的錯(cuò)誤)
4、TCP端口監(jiān)聽復(fù)制消息可用的端口范圍:4000 - 4100
5、兩個(gè)偵聽器需要配置ClusterSessionListener
和 JvmRouteSessionIDBinderListener
6、兩個(gè)攔截器需要配置TcpFailureDetector
和MessageDispatch15Interceptor
以下是集群的默認(rèn)配置
<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>
- 集群基礎(chǔ)知識(shí)
在Tomcat6.0上運(yùn)用會(huì)話復(fù)制,需遵循以下規(guī)則:
?你所有的session屬性必須實(shí)現(xiàn)java.io.serializable
?去掉server.xml中Cluster部分注釋
?如你有自定義Cluster valve,請(qǐng)確保在server.xml中 replicationvalve定義為在Cluster元素后
?如你Tomcat實(shí)例運(yùn)行在同一臺(tái)機(jī)器,確保tcplistenport屬性是的唯一性,通常情況下,Tomcat是足夠自動(dòng)檢測的端口范圍是4000-4100
?確保你的web.xml有<distributable/> 元素
?如果您使用的mod_jk,確保jvmroute屬性在<Engine name="Catalina" jvmRoute="node01" >中,并且,jvmroute屬性值需和workers.properties中work name 匹配
?確保所有節(jié)點(diǎn)時(shí)鐘是同步的
?確保負(fù)載均衡配置成粘性會(huì)話模式。
負(fù)載均衡的實(shí)現(xiàn)方式有很多種,可參考 負(fù)載均衡章節(jié)。
注意:你的會(huì)話用cookie進(jìn)行跟蹤,因此你需要確保URL的一致性,否則一個(gè)新的session就會(huì)創(chuàng)建。
注意:集群當(dāng)前支持的版本是JDK1.5以以上
集群模塊采用Tomcat的JULI日志框架,因此你可以在logging.properties中配置日志規(guī)則。跟蹤messages,可開啟配置中的:org.apache.catalina.tribes.MESSAGES - 概述
在Tomcat中啟用會(huì)話復(fù)制,有以下三種不同的實(shí)現(xiàn)方式:
1:使用會(huì)話持久性,并保存會(huì)話至共享文件系統(tǒng)(使用PersistenceManager + FileStore)
2:使用會(huì)話持久性,并保存會(huì)話至共享數(shù)據(jù)庫(使用PersistenceManager + JDBCStore)
3:使用內(nèi)存復(fù)制+Tomcat6附帶的SimpleTcpCluster(lib/catalina-tribes.jar + lib/catalina-ha.jar)
在這個(gè)發(fā)布的會(huì)話復(fù)制版本中,Tomcat可以使用DeltaManager來實(shí)現(xiàn)會(huì)話復(fù)制all-to-all或用備份復(fù)制來實(shí)現(xiàn)會(huì)話共享(僅在一個(gè)節(jié)點(diǎn)上使用BackupManager)。all-to-all會(huì)話復(fù)制模式適用于小的集群模式。對(duì)于大集群模式可用primary-secondary來實(shí)現(xiàn)-會(huì)話會(huì)基于BackupManager的方式-存儲(chǔ)在一臺(tái)備份服務(wù)器上。
當(dāng)前,你使用 domain workerattribute(mod_jk1.2.8以上)來構(gòu)建集群劃分。這種構(gòu)建方式可以解決潛在的系統(tǒng)構(gòu)建的伸縮問題。為了確保網(wǎng)絡(luò)通訊,你可以將集群分成若干小組。他們可以通過不同的組播地址來區(qū)分。
這種簡單的結(jié)構(gòu)如下所示:
DNS Round Robin
|
Load Balancer
/ \
Cluster1 Cluster2
/ \ / \
Tomcat1 Tomcat2 Tomcat3 Tomcat4
需注意session的復(fù)制只是集群開始,更重要的是:一個(gè)流程的集群: farming,如你在一個(gè)服務(wù)器上部署了應(yīng)用,然后集群會(huì)把該應(yīng)用分別部署在集群中的各個(gè)節(jié)點(diǎn)。可通過研究FarmWarDeployer來獲取相應(yīng)信息 (集群例子在server.xml)。
- 集群信息
節(jié)點(diǎn)成員通過組播心跳建立,如你想細(xì)分你的集群,可以通過修改<Membership>元素的組播地址或端口來實(shí)現(xiàn)。
心跳包含Tomcat節(jié)點(diǎn)的IP地址和tomcat監(jiān)聽session復(fù)制的TCP端口。所有的數(shù)據(jù)通信通過TCP協(xié)議實(shí)現(xiàn)。
ReplicationValve 用于找出已完成的請(qǐng)求和啟動(dòng)復(fù)制。當(dāng)session發(fā)生變化時(shí)數(shù)據(jù)才會(huì)被復(fù)制(在session中調(diào)用setAttribute或 removeAttribute 會(huì)改變session數(shù)據(jù))
一個(gè)重要性能考慮因素就是同步復(fù)制還是異步復(fù)制的,同步復(fù)制模式是所有請(qǐng)求需等所有會(huì)話都已復(fù)制到其他節(jié)點(diǎn)時(shí)才會(huì)返回。同步和異步都采用 channelSendOptions標(biāo)記(INT數(shù)據(jù)類型),SimpleTcpCluster/DeltaManagerd的默認(rèn)值為8時(shí)它是異步的。你可以通過閱讀send flag(overview) 或 send flag(javadoc)了解更多信息。
在異步復(fù)制模式中,請(qǐng)求會(huì)先于會(huì)話復(fù)制完成前返回,異步復(fù)制的請(qǐng)求時(shí)間相對(duì)應(yīng)同步復(fù)制會(huì)更短,同步處理保證請(qǐng)求返回前,session已經(jīng)同步完成。 - 節(jié)點(diǎn)crash后的故障節(jié)點(diǎn)轉(zhuǎn)移-綁定session
如你使用的是mod_jk和非sticky session,或者是因某些原因sticky session不工作,更或者是簡單tomcat崩潰,session的id將要修改它以前的worker的ID,也就是之前tomcat的ID(定義在 Engine元素的jvmRoute屬性)。為解決這個(gè)問題,我們需要使用 JvmRouteBinderValve。
Jvmroutebindervalve重寫會(huì)話ID,以確保崩潰后下一個(gè)請(qǐng)求進(jìn)來時(shí)能被粘?。ú粫?huì)隨機(jī)返回節(jié)點(diǎn)-之前的節(jié)點(diǎn)已不可用)。該配置會(huì)重寫cookies中的JSESSIONID值和會(huì)話ID操持一直,如沒有這值,在崩潰后很難使用mod_jk就能保證session的sticky。
默認(rèn)情況下,沒有配置valve,那么就會(huì)被Jvmroutebindervalve添加。
集群消息偵聽器(JvmRouteSessionIDBinderListener)實(shí)際為默認(rèn)配置,在故障發(fā)生時(shí) 用來重寫集群中跟其他節(jié)點(diǎn)session id。記住,當(dāng)你添加的valve或在server.xml中配置集群監(jiān)聽器,默認(rèn)的Jvmroutebindervalve就會(huì)變得無效,因此請(qǐng)確定你在所有節(jié)點(diǎn)中添加了正確的valve和監(jiān)聽器。
提示:
sessionIdAttribute屬性是可在request中更改名稱-包含舊的session id. 默認(rèn)屬性名稱為
org.apache.catalina.cluster.session.JvmRouteOrignalSessionID。
技巧:
在所有備份節(jié)點(diǎn)中刪除一個(gè)節(jié)點(diǎn)時(shí),你可以啟用mod_jk調(diào)動(dòng)via JMX!
在所有JvmRouteBinderValve備份中設(shè)置為true,禁止mod_jk工作,刪除節(jié)點(diǎn),并重啟。
再次啟用mod_jk和禁止JvmRouteBinderValve。
這個(gè)用例意味著只有請(qǐng)求的會(huì)話會(huì)被遷移。
- 配置示例
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="6">
<Manager className="org.apache.catalina.ha.session.BackupManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"
mapSendOptions="6"/>
<!--
<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="5000"
selectorTimeout="100"
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"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
<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.ClusterSessionListener"/>
</Cluster>
配置詳解
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="6">
Cluster是主要的元素,集群的配置的所有細(xì)節(jié)都在這個(gè)元素中。channelSendOpentions是一個(gè)標(biāo)記,連接所有SimpleTcpCluster發(fā)送的信息,或者任何調(diào)用SimpleTcpCluster.send方法的對(duì)象。標(biāo)記描述定義在javadoc site。
DelatManager使用SimpleTcpCluster.send方法發(fā)送消息,而backup manager則是通過通道直接發(fā)送消息。
更多參考信息,請(qǐng)閱讀引用文檔
<Manager className="org.apache.catalina.ha.session.BackupManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"
mapSendOptions="6"/>
<!--
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
-->
這個(gè)是manager 配置的模板,當(dāng)context元素中無manager配置時(shí)默認(rèn)使用。在tomcat5.x中,每一個(gè)app應(yīng)用必須用同一個(gè)manager,但在該版本的tomcat中,你可以為每一個(gè)webapp定義manager。所以,你可以在你的集群中混合配置manager。
很顯然,每一個(gè)節(jié)點(diǎn)中的manager需要同集群中其他節(jié)點(diǎn)的manager保持一致。如果沒有為webapp配置manager,并且webapp被標(biāo)記成<distributable/>,tomcat將會(huì)采用這個(gè)manager的配置并且生成一個(gè)manager的實(shí)例。
更多參考信息,請(qǐng)閱讀引用文檔
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
Channel是一個(gè)部落,一個(gè)在tomcat內(nèi)部的組通信框架。
該元素將所有的通信相關(guān)以及節(jié)點(diǎn)之間關(guān)系封裝起來。
更多參考信息,請(qǐng)閱讀引用文檔
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
membership是通過組播的。
請(qǐng)注意,部落也支持靜態(tài)成員,如你想擴(kuò)展節(jié)點(diǎn)可用staticmembershipinterceptor實(shí)現(xiàn)。
地址屬性使用組播地址,端口使用組播端口,兩者共同組成了集群的分割。
如你想要一個(gè)質(zhì)量保證集群和一個(gè)生產(chǎn)集群,最簡單的配置方式是將QA集群做成一個(gè)獨(dú)立的組播地址和端口然后和生產(chǎn)集群組合。
Membership組成的廣播TCP地址和端口能到其他的節(jié)點(diǎn),因此節(jié)點(diǎn)間可以通過TCP進(jìn)行通信。
請(qǐng)注意,地址是一個(gè)廣播的receiver.address屬性。
更多參考信息,請(qǐng)閱讀引用文檔
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="5000"
selectorTimeout="100"
maxThreads="6"/>
在Tribes(有組通信能力的消息傳遞框架)中的發(fā)送和接收數(shù)據(jù)已被分解成兩大組件。
接收,顧名思義:負(fù)責(zé)接收消息。
由于Tribes的堆棧線程較少, (現(xiàn)在已采用其他框架所流行的方式進(jìn)行改進(jìn)), 有一個(gè)線程池,該組件具有一個(gè)maxthreads和minthreads設(shè)置。 地址屬性的host地址將通過membership組件廣播給其他節(jié)點(diǎn)。
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
發(fā)送組件,顧名思義:負(fù)責(zé)將消息傳遞給其他節(jié)點(diǎn)。
發(fā)送組件有一個(gè)shell組件,復(fù)制傳輸?shù)染唧w要做的事情在子組件完成。Tribes有一個(gè)發(fā)送池,支持并行發(fā)送消息,如果使用NIO發(fā)送者,你也可以同時(shí)發(fā)送消息。
同時(shí)意味著一個(gè)消息到多個(gè)發(fā)送者的并行和多個(gè)消息到多個(gè)發(fā)送者的并行。
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>
</Channel>
Tribes通過堆棧來發(fā)送消息。
堆棧中的每個(gè)元素被稱為一個(gè)攔截器,工作模式同Tomcat servlet容器。使用攔截器,可將邏輯分解成更易于管理的代碼塊。
上面的攔截器配置是:
TcpFailureDetector(TCP故障檢測器)——通過TCP驗(yàn)證節(jié)點(diǎn)crashed。如果組播數(shù)據(jù)包被丟棄,攔截器為防止誤報(bào),會(huì)將節(jié)點(diǎn)標(biāo)記為崩潰,即使它仍然是活著的。
MessageDispatch15Interceptor——分配線程(線程池)用于同步發(fā)送消息。
ThroughputInterceptor——打印出簡單的信息通信記錄。
請(qǐng)注意,攔截器的順序是很重要的。他們?cè)诜?wù)器中定義的方式。xml是他們?cè)谕ǖ蓝褩5姆绞?。認(rèn)為這是一個(gè)鏈表,頭被第一個(gè)攔截器和尾最后一次。
他們被定義在server.xmlzhong,表示為通道堆棧。把它作為一個(gè)鏈表,頭是第一個(gè)最攔截和尾部的最后一個(gè)。
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>
集群使用valve來跟蹤到web應(yīng)用的請(qǐng)求,我們之前提到了ReplicationValve和JvmRouteBinderValve。
<Cluster>元素不是Tomcat元素,相反是集群將valve添加到父容器中。當(dāng)<Cluster>元素在<Engine>元素中被配置,valve將被添加到引擎中等等。
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
默認(rèn)的Tomcat集群支持farmed deployment,如:集群能將部署和取消部署其他節(jié)點(diǎn)的應(yīng)用。
該組件當(dāng)前不穩(wěn)定但很快會(huì)解決這一問題。
Tomcat 5.0 和 5.5 版本相比,在部署算法上有一點(diǎn)變化。
組件的邏輯改變到部署目錄必須與應(yīng)用目錄相匹配。
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
因?yàn)?SimpleTcpCluster 本身既是 Channel 對(duì)象的發(fā)送者,又是接受者,所以組件可以將它們自身注冊(cè)成SimpleTcpCluster的偵聽器。
上面這個(gè)偵聽器 ClusterSessionListener 將偵聽 DeltaManager 復(fù)制的消息,并將會(huì)話變更應(yīng)用到 manager 上,反過來應(yīng)用到會(huì)話上。
集群架構(gòu)
Server
|
Service
|
Engine
| \
| --- Cluster --*
|
Host
|
------
/ \
Cluster Context(1-N)
| \
| -- Manager
| \
| -- DeltaManager
| -- BackupManager
|
|---------------------------
| \
Channel \
----------------------------- \
| \
Interceptor_1 .. \
| \
Interceptor_N \
----------------------------- \
| | | \
Receiver Sender Membership \
-- Valve
| \
| -- ReplicationValve
| -- JvmRouteBinderValve
|
-- LifecycleListener
|
-- ClusterListener
| \
| -- ClusterSessionListener
| -- JvmRouteSessionIDBinderListener
|
-- Deployer
\
-- FarmWarDeployer
工作原理
為了易于理解集群的工作原理,我們通過模擬場景來加深理解。
在這場景中我們需用到兩個(gè)tomcat實(shí)例:Tomcat A和Tomcat B,我們會(huì)按順序模擬以下事件:
1、Tomcat A 啟動(dòng)。
2、Tomcat B 啟動(dòng)(在Tomcat A啟動(dòng)成功之后)
3、Tomcat A 接收一個(gè)請(qǐng)求,建立session S1.
4、Tomcat A crash
5、Tomcat B 接收到一個(gè)session為S1 的請(qǐng)求
6、Tomcat A 啟動(dòng)
7、Tomcat A 接收一個(gè)請(qǐng)求,調(diào)用session為S1的invalidate
8、Tomcat B 接收一個(gè)請(qǐng)求,新Session S2
9、Tomcat A session S2 由于不活躍而超時(shí)
介紹完了事件序列,下面詳細(xì)剖析一下在會(huì)話復(fù)制代碼中到底發(fā)生了什么。
- Tomcat A 啟動(dòng)
Tomcat 使用標(biāo)準(zhǔn)啟動(dòng)順序來啟動(dòng)。
Host 對(duì)象創(chuàng)建好之后,會(huì)關(guān)聯(lián)一個(gè) Cluster 對(duì)象。
在解析上下文時(shí),如果 web.xml 中包含 distributable 元素,
Tomcat 就會(huì)讓 Cluster 類(在該例中是 SimpleTcpCluster)創(chuàng)建復(fù)制的上下文的管理器。
啟用了集群并在 web.xml 中設(shè)置了 distributable 元素后,Tomcat 會(huì)為該上下文創(chuàng)建一個(gè) DeltaManager(而不是 StandardManager)。
Cluster 類會(huì)啟動(dòng)一個(gè)成員服務(wù)(組播)和一個(gè)復(fù)制服務(wù)(TCP 單播)。下文將會(huì)介紹更多的架構(gòu)細(xì)節(jié)。
- Tomcat B 啟動(dòng)
Tomcat B 啟動(dòng)時(shí),采取的順序與 Tomcat A 基本一樣。集群啟動(dòng),建立成員(Tomcat A 與 Tomcat B)。
Tomcat B 會(huì)請(qǐng)求集群中已有服務(wù)器(本例中是 Tomcat A)的會(huì)話狀態(tài)。
如果 Tomcat A 響應(yīng)該請(qǐng)求,那么在 Tomcat B 開始偵聽 HTTP 請(qǐng)求之前,Tomcat A 會(huì)將會(huì)話狀態(tài)傳到 Tomcat B那里;
如果 Tomcat A 沒有響應(yīng)該請(qǐng)求,Tomcat B 會(huì)等待 60 秒后,記錄日志信息。
該會(huì)話狀態(tài)會(huì)發(fā)送到每一個(gè)在 web.xml 中設(shè)置了 distributable 元素的應(yīng)用。
注意:為了有效地使用會(huì)話復(fù)制,所有的 Tomcat 實(shí)例都必須擁有相同的配置。 - Tomcat A 接收一個(gè)請(qǐng)求,建立session S1
Tomcat A 接收到請(qǐng)求的處理方式,與沒有會(huì)話復(fù)制時(shí)的處理方式完全相同。
請(qǐng)求完成時(shí)會(huì)觸發(fā)相應(yīng)行為,ReplicationValve 會(huì)在響應(yīng)返回用戶之前攔截請(qǐng)求。
如發(fā)現(xiàn)會(huì)話已經(jīng)更改,則使用 TCP 將會(huì)話復(fù)制到 Tomcat B 上。
一旦序列化的數(shù)據(jù)被轉(zhuǎn)交給操作系統(tǒng)的 TCP 邏輯,請(qǐng)求就會(huì)重新通過 valve 管道返回給用戶。
對(duì)于每一個(gè)請(qǐng)求,都將復(fù)制所有的會(huì)話,這樣做就有利于復(fù)制那些在會(huì)話中修改屬性的代碼,
使其即使不必調(diào)用 setAttribute 或 removeAttribute,也能被復(fù)制。
另外,使用 useDirtyFlag 配置參數(shù)也可以優(yōu)化會(huì)話的復(fù)制次數(shù)。 - Tomcat A crash
當(dāng) Tomcat A 崩潰時(shí),Tomcat B 會(huì)接到通知,得知 Tomcat A 已被移出集群,隨即 Tomcat B 就在其成員列表中也將 Tomcat A 移除,Tomcat B 不再收到關(guān)于 Tomcat A 的任何通知。
負(fù)載均衡器會(huì)把 Tomcat A 中請(qǐng)求全部重定向到Tomcat B ,所有的會(huì)話都將保持現(xiàn)有的狀態(tài)。 - Tomcat B 接收到一個(gè)session為S1 的請(qǐng)求
無意外的,Tomcat B 將會(huì)像按處理任何請(qǐng)求的方式來處理該請(qǐng)求。 - Tomcat A 啟動(dòng)
在 Tomcat A 開始接收新的請(qǐng)求之前,將會(huì)根據(jù)上面(1)(2)兩條所所說明的啟動(dòng)序列來啟動(dòng)。
Tomcat A 會(huì)加入集群,聯(lián)系 Tomcat B 并獲取所有的會(huì)話狀態(tài)。
一旦接收到會(huì)話狀態(tài),就會(huì)完成加載,并打開 HTTP/mod_jk 端口。
所以,除非 Tomcat A 從 Tomcat B 那里接收到了會(huì)話變更,否則沒有發(fā)給 Tomcat A 的請(qǐng)求。 - Tomcat A 接收一個(gè)請(qǐng)求,調(diào)用session為S1的invalidate
攔截器會(huì)攔截invalidate 的調(diào)用, 并且 session 會(huì)被加入失效會(huì)話隊(duì)列。
在請(qǐng)求完成時(shí),不會(huì)發(fā)送會(huì)話改變消息,而是發(fā)送一個(gè) “到期” 消息給 Tomcat B,Tomcat B 也會(huì)讓此會(huì)話失效。 - Tomcat B 接收一個(gè)請(qǐng)求,新Session S2
同步驟3. - Tomcat A session S2 由于不活躍而超時(shí)
invalidate 調(diào)用會(huì)被攔截,當(dāng)一個(gè)會(huì)話被用戶標(biāo)記失效時(shí),該會(huì)話就會(huì)加入到無效會(huì)話隊(duì)列。
此時(shí),失效的會(huì)話不會(huì)被復(fù)制,直到另一個(gè)請(qǐng)求通過系統(tǒng)并檢查無效會(huì)話隊(duì)列。
Membership 集群成員是通過非常簡單的組播 ping 命令來實(shí)現(xiàn)的。每個(gè) Tomcat 實(shí)例都會(huì)定期發(fā)送一個(gè)組播 ping,ping 消息中包含 Tomcat 實(shí)例自身的 IP 和配置的 TCP 監(jiān)聽端口。如果實(shí)例在一個(gè)給定的時(shí)間內(nèi)沒有收到這樣的 ping 信息,就會(huì)認(rèn)為那個(gè)成員已經(jīng)崩潰了。非常簡潔高效!當(dāng)然,您需要在系統(tǒng)上啟用組播。
TCP 復(fù)制 一旦收到一個(gè)多播 ping 包,在下一個(gè)復(fù)制請(qǐng)求時(shí)成員被添加到集群,發(fā)送實(shí)例將使用的主機(jī)和端口信息,以及建立TCP socket。使用該TCP socket發(fā)送序列化的數(shù)據(jù)。選擇TCP socket,是因?yàn)樗⒂辛髁靠刂坪捅WC發(fā)送的機(jī)制。因此我知道我想發(fā)送的數(shù)據(jù)它能發(fā)送到指定位置。
分布式鎖定與頁面使用框架 Tomcat 在跨集群同步不保持會(huì)話實(shí)例。這種邏輯的實(shí)現(xiàn)將開銷大,并導(dǎo)致各種各樣的問題。
如果客戶端用同一個(gè)會(huì)話同時(shí)發(fā)送多個(gè)請(qǐng)求,那么最后的請(qǐng)求將會(huì)覆蓋集群中的其他會(huì)話。