(點選上方公眾號,可快速關註)
來源:袁鳴凱 ,
blog.csdn.net/lifetragedy/article/details/7712691
一、為何要叢集
單臺App Server再強勁,也有其瓶勁,先來看一下下麵這個真實的場景。
當時這個工程是這樣的,tomcat這一段被稱為web zone,裡面用spring+ws,還裝了一個jboss的規則引擎Guvnor5.x,全部是ws沒有service layer也沒有dao layer。
然後App Zone這邊是weblogic,傳輸用的是spring rmi,然後App Zone這塊全部是service layer, dao layer和資料庫打交道。
使用者這邊用的是.net,以ws和web zone連的。
時間一長,資料一多,就出問題了。
拿Loader Runner跑下來,發覺是Web Zone這塊,App Server已經被用到極限了。因為客戶錢不多,所以當時的Web Zone是2臺伺服器,且都是32位的,記憶體不少,有8GB,測試下來後發覺cpu loader又不高,但是web server這邊的吞吐量始終上不去,且和.net客戶端那邊響應越來越慢。
分析了一下原因:單臺tomcat能夠承受的最大負載已經到頭了,單臺tomcat的吞吐量就這麼點,還要負擔Guvnor的執行,Guvnor內有數百條業務規則要執行。
再看了一下其它方面的程式碼、SQL調優都已經到了極限了,所以最後沒辦法,客戶又不肯拿錢投在記憶體和新機器上或者是再買臺Weblogic,只能取捨一下,搞Tomcat叢集了。
二、叢集分類
Tomcat作叢集的邏輯架構是上面這樣的一張圖,關鍵是我們的production環境還需要規劃好我們的物理架構。
2.1 橫向叢集
比如說,有兩臺Tomcat,分別執行在2臺物理機上,好處是最大的即CPU擴充套件,記憶體也擴充套件了,處理能力也擴充套件了。
2.2 縱向叢集
即,兩個Tomcat的實體執行在一臺物理器上,充分利用原有記憶體,CPU未得到擴充套件。
2.3 橫向還是縱向
一般來說,廣為人們接受的是橫向擴充套件的叢集,可做大規模叢集佈署。但是我們這個case受制於客戶即:
-
不會再投入新機器了
-
不會增加記憶體了
但是呢,透過壓力測試報告我們可知:
-
原有TomcatServer的CPU Loader不高,在23%左右
-
原有TomcatServer上有8GB記憶體,而且是32位的,單臺Tomcat只使用了1800MB左右的記憶體
-
網路流量不高,單塊千兆乙太網卡完全可以處理掉
因此,我們只能做熊掌與魚不能兼得的事,即採用了:縱向叢集。
2.4 Load Balance與High Available
-
Load Balance
簡稱LB即負載均衡,相當於1000根執行緒每個叢集節點:Node負責處理500個,這樣的效率是最高的。
-
High Available
簡稱HA即高可用性,相當於1000根執行緒還是交給一臺機器去慢慢處理,如果這臺機器崩了,另一臺機器頂上。
三、叢集架構中需要解決的問題
叢集規劃好了怎麼分,這不等於就可以開始實現叢集了,一旦你的系統實現了叢集,隨之而來的問題就會出現了。
我們原有系統中有這樣幾個問題,在叢集環境中是需要解決的,來看:
3.1 解決上傳檔案同步的問題
叢集後就是兩個Tomcat了,即和兩個執行緒讀同一個resource的問題是一樣的,還好,我們原有上傳檔案是專門有一臺檔案伺服器的,這個問題不大,兩個tomcat都往一臺file server裡上傳,檔案伺服器已經幫我們解決了同名檔案衝突的這個問題了,如果原先的做法是把檔案上傳到Tomcat的目錄中,那問題就大了,來看:
叢集環境中,對於使用者來說一切操作都是透明的,他也不知道我有幾個Tomcat的實體執行在那邊。
使用者一點上傳,可能上傳到了Tomcat2中,但是下次要顯示這個檔案時,可能用到的是Tomcat1內的jsp或者是class,對不對?
於是,因為你把圖片存在了Tomcat的目錄中,因此導致了Tomcat1在顯示圖片時,取不到Tomcat2目錄中存放的圖片。
因此我們在工程一開始就強調存圖片時要用一臺專門的檔案伺服器或者是FTP伺服器來存,就是為了避免將來出現這樣的問題。
3.2 解決Quartz在叢集環境中的同步問題
我們的系統用到一個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在叢集環境下的最終解決方案
http://blog.csdn.net/lifetragedy/article/details/6212831
解決了上述的問題後基本我們可以開始佈署Tomcat這個叢集了。
四、佈署Tomcat叢集
準備兩個版本一致的Tomcat,分別起名為tomcat1,tomcat2。
4.1 Apache中的配置
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
找到原來的“
改成如下形式:
DocumentRoot d:/www
AllowOverride None
Order allow,deny
Allow from all
Order deny,allow
Deny from all
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
註意:
原來的JKMount *** 後的 ajp13變成了什麼了?
controller
4.2 tomcat中的配置
可以拿原有的tomcat複製成另一個tomcat,分別為d:\tomcat, d:\tomcat2。
開啟tomcat中的conf目錄中的server.xml,找到下麵這行
1)
記得:
一定要把tomcat2中的這邊的”SHUTDOWN”的port改成另一個埠號,兩個tomcat如果是在叢集環境中,此處的埠號絕不能一樣。
2)找到
確保tomcat2中此處的埠不能為8080,我們就使用9090這個埠吧
3)把兩個tomcat中原有的https的配置,整段去除
4)找到
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)找到
改為:
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″>
改成
同時把tomcat2中此處內容改成
7)
在剛才的
的下麵與在
之上,在這之間加入如下一大陀的東西:
channelSendOptions=”6″>
expireSessionsOnShutdown=”false”
notifyListenersOnReplication=”true”
mapSendOptions=”6″/>
bind=”127.0.0.1″
address=”228.0.0.4″
port=”45564″
frequency=”500″
dropTime=”3000″/>
address=”auto”
port=”4001″
selectorTimeout=”100″
maxThreads=”6″/>
filter=”.*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;”/>
此處有一個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檔案的最未尾即“
”這一行前加入如下的一行:
使該工程中的session可以被tomcat的叢集節點進行輪循複製。
4.3 啟動叢集
好了,現在啟動tomcat1, 啟動tomcat2(其實無所謂順序的),來看效果:
分別訪問http://localhost:8080/cbbs與http://localhost:9090/cbbs
確保兩個tomcat節點都起來了,然後此時,我們啟動Apache
然後訪問直接用http://localhost/cbbs不加埠的形式訪問:
用sally/abcdefg登入,瞧,應用起來了。
然後我們拿另一臺物理客戶端,登入這個web應用,我們可以看到:
第一個tomcat正在負責處理我們第一次登入的請求。
當有第二個HTTP請求時,另一個tomcat自動開始肩負起我們第二個HTTP請求了,這就是Load Balance。
系列
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能