單臺App Server再強勁,也有其瓶勁,先來看一下下面這個真實的場景。javascript
當時這個工程是這樣的,tomcat這一段被稱爲web zone,裏面用spring+ws,還裝了一個jboss的規則引擎Guvnor5.x,所有是ws沒有service layer也沒有dao layer。css
而後App Zone這邊是weblogic,傳輸用的是spring rmi,而後App Zone這塊所有是service layer, dao layer和數據庫打交道。html
用戶這邊用的是.net,以ws和web zone連的。java
時間一長,數據一多,就出問題了。web
拿Loader Runner跑下來,發覺是Web Zone這塊,App Server已經被用到極限了。由於客戶錢很少,因此當時的Web Zone是2臺服務器,且都是32位的,內存很多,有8GB,測試下來後發覺cpu loader又不高,可是web server這邊的吞吐量始終上不去,且和.net客戶端那邊響應愈來愈慢。spring
分析了一下緣由:單臺tomcat可以承受的最大負載已經到頭了,單臺tomcat的吞吐量就這麼點,還要負擔Guvnor的運行,Guvnor內有數百條業務規則要執行。數據庫
再看了一下其它方面的代碼、SQL調優都已經到了極限了,因此最後沒辦法,客戶又不願拿錢投在內存和新機器上或者是再買臺Weblogic,只能取捨一下,搞Tomcat集羣了。apache
Tomcat做集羣的邏輯架構是上面這樣的一張圖,關鍵是咱們的production環境還須要規劃好咱們的物理架構。tomcat
好比說,有兩臺Tomcat,分別運行在2臺物理機上,好處是最大的即CPU擴展,內存也擴展了,處理能力也擴展了。服務器
即,兩個Tomcat的實例運行在一臺物理器上,充分利用原有內存,CPU未獲得擴展。
通常來講,廣爲人們接受的是橫向擴展的集羣,可作大規模集羣佈署。可是咱們這個case受制於客戶即:
ü 不會再投入新機器了
ü 不會增長內存了
可是呢,經過壓力測試報告咱們可知:
ü 原有TomcatServer的CPU Loader不高,在23%左右
ü 原有TomcatServer上有8GB內存,並且是32位的,單臺Tomcat只使用了1800MB左右的內存
ü 網絡流量不高,單塊千兆以太網卡徹底能夠處理掉
所以,咱們只能作熊掌與魚不能兼得的事,即採用了:縱向集羣。
ü Load Balance
簡稱LB即負載均衡,至關於1000根線程每一個集羣節點:Node負責處理500個,這樣的效率是最高的。
ü High Available
簡稱HA即高可用性,至關於1000根線程仍是交給一臺機器去慢慢處理,若是這臺機器崩了,另外一臺機器頂上。
集羣規劃好了怎麼分,這不等於就能夠開始實現集羣了,一旦你的系統實現了集羣,隨之而來的問題就會出現了。
咱們原有系統中有這樣幾個問題,在集羣環境中是須要解決的,來看:
集羣后就是兩個Tomcat了,即和兩個線程讀同一個resource的問題是同樣的,還好,咱們原有上傳文件是專門有一臺文件伺服器的,這個問題不大,兩個tomcat都往一臺file server裏上傳,文件伺服器已經幫咱們解決了同名文件衝突的這個問題了,若是原先的作法是把文件上傳到Tomcat的目錄中,那問題就大了,來看:
集羣環境中,對於用戶來講一切操做都是透明的,他也不知道我有幾個Tomcat的實例運行在那邊。
用戶一點上傳,可能上傳到了Tomcat2中,可是下次要顯示這個文件時,可能用到的是Tomcat1內的jsp或者是class,對不對?
因而,由於你把圖片存在了Tomcat的目錄中,所以致使了Tomcat1在顯示圖片時,取不到Tomcat2目錄中存放的圖片。
所以咱們在工程一開始就強調存圖片時要用一臺專門的文件服務器或者是FTP服務器來存,就是爲了不未來出現這樣的問題。
咱們的系統用到一個Quartz(一個定時服務組件)來定時觸發一些自動機制,如今有了兩個Tomcat,粗想一想每一個Tomcat裏運行本身的Quartz不就好了?
可是問題來了,若是兩個Quartz在同一時間都觸發了處理同一條定單,即該條定單會被處理兩邊。。。這不是影響效率和增長出錯機率了嗎?
由於自己Quartz所承受的壓力幾乎能夠忽略不計的,它只是定時會觸發腳本去運行,關鍵在於這個定時腳本的同步性,一致性的問題上。
咱們曾想過的解決方法:
咱們可讓一個Tomcat佈署Quartz,另外一個Tomcat裏不佈署Quartz
但這樣作的結果就是若是佈署Quartz的這個Tomcat崩潰掉了,這個Quartz是否是也崩啦?
最後解決的辦法:
因此咱們仍是必須在兩臺Tomcat里布署Quartz,而後使用HA的原則,即一個Quartz在運行時,另外一臺Quartz在監視着,而且不斷的和另外一個Quartz之間保持勾通,一旦運行着的Quartz崩掉了,另外一個Quartz在指定的秒數內起來接替原有的Quartz繼續運行,對於Quartz,咱們一樣也是面臨着一個熊掌與魚不能皆得的問題了,Quartz自己是支持集羣的,而它支持的集羣方式正是HA,和咱們想的是一致的。
具體Quartz是如何在集羣環境下做佈署的,請見個人另外一篇文章:quartz在集羣環境下的最終解決方案
解決了上述的問題後基本咱們能夠開始佈署Tomcat這個集羣了。
準備兩個版本一致的Tomcat,分別起名爲tomcat1,tomcat2。
² worker.properties文件內容的修改
打開Apache HttpServer中的apache安裝目錄/conf/work.properties文件,你們還記得這個文件嗎?
這是原有文件內容:
workers.tomcat_home=d:/tomcat2 workers.java_home=C:/jdk1.6.32 ps=/ worker.list=ajp13 worker.ajp13.port=8009 worker.ajp13.host=localhost worker.ajp13.type=ajp13 |
如今開始改動成下面這樣的內容(把原有的worker.properties中的內容前面都加上#註釋掉):
#workers.tomcat_home=d:/tomcat2 #workers.java_home=C:/jdk1.6.32 #ps=/ #worker.list=ajp13 #worker.ajp13.port=8009 #worker.ajp13.host=localhost #worker.ajp13.type=ajp13 worker.list = controller #tomcat1 worker.tomcat1.port=8009 worker.tomcat1.host=localhost worker.tomcat1.type=ajp13 worker.tomcat1.lbfactor=1 #tomcat2 worker.tomcat2.port=9009 worker.tomcat2.host=localhost worker.tomcat2.type=ajp13 worker.tomcat2.lbfactor=1 #========controller======== worker.controller.type=lb worker.controller.balance_workers=tomcat1,tomcat2 worker.lbcontroller.sticky_session=0 worker.controller.sticky_session_force=true worker.connection_pool_size=3000 worker.connection_pool_minsize=50 worker.connection_pool_timeout=50000
|
上面的這些設置的意思用中文來表達就是:
ü 兩個tomcat,都位於localhost
ü 兩個tomcat,tomcat1用8009,tomcat2用9009與apache保持jk_mod的通信
ü 不採用sticky_session的機制
sticky_session即:假設如今用戶正連着tomcat1,而tomcat1崩了,那麼此時它的session應該被複制到tomcat2上,由tomcat2繼續負責該用戶的操做,這就是load balance,此時這個用戶因該能夠繼續操做。
若是你的sticky_session設成了1,那麼當你連的這臺tomcat崩了後,你的操做由於是sticky(粘)住被指定的集羣節點的,所以你的session是不會被複制和同步到另外一個還存活着的tomcat節點上的。
ü 兩臺tomcat被分派到的任務的權重(lbfactor)爲一致
你也能夠設tomcat1 的worker.tomcat2.lbfactor=10,而tomcat2的worker.tomcat2.lbfactor=2,這個值越高,該tomcat節點被分派到的任務數就越多
² httpd.conf文件內容的修改
找到下面這一行:
Include conf/extra/httpd-ssl.conf |
咱們將它註釋掉,由於咱們在集羣環境中不打算採用https,若是採用是https也同樣,只是爲了減省開銷(不少人都是用本身的開發電腦在作實驗哦)。
#Include conf/extra/httpd-ssl.conf |
找到原來的「<VirtualHost>」段
改爲以下形式:
<VirtualHost *> DocumentRoot d:/www <Directory "d:/www/cbbs"> AllowOverride None Order allow,deny Allow from all </Directory> <Directory "d:/www/cbbs/WEB-INF"> Order deny,allow Deny from all </Directory> ServerAdmin localhost DocumentRoot d:/www/ ServerName shnlap93:80 DirectoryIndex index.html index.htm index.jsp index.action ErrorLog logs/shsc-error_log.txt CustomLog logs/shsc-access_log.txt common
JkMount /*WEB-INF controller JkMount /*j_spring_security_check controller JkMount /*.action controller JkMount /servlet/* controller JkMount /*.jsp controller JkMount /*.do controller JkMount /*.action controller
JkMount /*fckeditor/editor/filemanager/connectors/*.* controller JkMount /fckeditor/editor/filemanager/connectors/* controller </VirtualHost> |
注意:
原來的JKMount *** 後的 ajp13變成了什麼了?
controller
能夠拿原有的tomcat複製成另外一個tomcat,分別爲d:\tomcat, d:\tomcat2。
打開tomcat中的conf目錄中的server.xml,找到下面這行
1)
<Server port="8005" shutdown="SHUTDOWN"> |
記得:
必定要把tomcat2中的這邊的」SHUTDOWN」的port改爲另外一個端口號,兩個tomcat若是是在集羣環境中,此處的端口號毫不能同樣。
2)找到
<Connector port="8080" protocol="HTTP/1.1" |
確保tomcat2中此處的端口不能爲8080,咱們就使用9090這個端口吧
3)把兩個tomcat中原有的https的配置,整段去除
4)找到
<Connector port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" disableUploadTimeout="true" connectionTimeout="20000" acceptCount="300" maxThreads="300" maxProcessors="1000" minProcessors="5" useURIValidationHack="false" compression="on" compressionMinSize="2048" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" redirectPort="8443" /> |
確保tomcat2中這邊的redirectPort爲9443
5)找到
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" |
改成:
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" URIEncoding="UTF-8" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" disableUploadTimeout="true" connectionTimeout="20000" acceptCount="300" maxThreads="300" maxProcessors="1000" minProcessors="5" useURIValidationHack="false" compression="on" compressionMinSize="2048" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
/> |
確保tomcat2的server.xml中此處的8009被改爲了9009且其它內容與上述內容一致(redirectPort不要忘了改爲9443)
6)找到
<Engine name="Standalone" defaultHost="localhost" jvmRoute="jvm1"> |
改爲
<!-- You should set jvmRoute to support load-balancing via AJP ie : <Engine name="Standalone" defaultHost="localhost" jvmRoute="jvm1"> --> <Engine name="Standalone" defaultHost="localhost" jvmRoute="tomcat1"> |
同時把tomcat2中此處內容改爲
<!-- You should set jvmRoute to support load-balancing via AJP ie : <Engine name="Standalone" defaultHost="localhost" jvmRoute="jvm1"> --> <Engine name="Standalone" defaultHost="localhost" jvmRoute="tomcat2"> |
7)
在剛纔的
<Engine name="Standalone" defaultHost="localhost" jvmRoute="tomcat1"> |
的下面與在
<!-- The request dumper valve dumps useful debugging information about the request and response data received and sent by Tomcat. Documentation at: /docs/config/valve.html --> <!-- <Valve className="org.apache.catalina.valves.RequestDumperValve"/> --> |
之上,在這之間加入以下一大陀的東西:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="6"> <Manager className="org.apache.catalina.ha.session.BackupManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" mapSendOptions="6"/> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" bind="127.0.0.1" address="228.0.0.4" port="45564" frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4001" selectorTimeout="100" maxThreads="6"/> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" timeout="60000"/> </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;"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster> |
此處有一個Receiver port=」xxxx」,兩個tomcat中此處的端口號必須惟一,即tomcat中咱們使用的是port=4001,那麼咱們在tomcat2中將使用port=4002
8)把系統環境變動中的CATALINA_HOME與TOMCAT_HOME這兩個變量去除掉
9)在每一個tomcat的webapps目錄下佈署一樣的一個工程,在佈署工程前先確保你把工程中的WEB-INF\we b.xml文件作了以下的修改,在web.xml文件的最未尾即「</web-app>」這一行前加入以下的一行:
<distributable/> |
使該工程中的session能夠被tomcat的集羣節點進行輪循複製。
好了,如今啓動tomcat1, 啓動tomcat2(其實無所謂順序的),來看效果:
分別訪問http://localhost:8080/cbbs與http://localhost:9090/cbbs
確保兩個tomcat節點都起來了,而後此時,咱們啓動Apache
而後訪問直接用http://localhost/cbbs不加端口的形式訪問:
用sally/abcdefg登陸,瞧,應用起來了。
而後咱們拿另外一臺物理客戶端,登陸這個web應用,咱們能夠看到:
第一個tomcat正在負責處理咱們第一次登陸的請求。
當有第二個HTTP請求時,另外一個tomcat自動開始肩負起咱們第二個HTTP請求了,這就是Load Balance。