轉載至: http://blog.csdn.net/chaijunkun/article/details/6987443
最近在開發的項目需要承受很高的併發量。綜合各種情況,決定使用Apache+Tomcat+JK的方式實現負載均衡,並且作爲一個統一的服務還要實現羣集(同步Session)。
在網上找了很多資料,都是零零散散的,沒有一個完整的過程。通過幾天的努力,完成了從編譯、部署到配置的整個過程,期間也遇到了一些問題。在接下來的文字中將這些過程記錄下來,做個筆記同時也分享給大家。
爲了重新演示整個過程,我新搭建了一個服務器,各項參數如下:
CPU:Intel Xeon 5506*2
內存:DDR3 4G*4
主機型號:Dell PowerEdge R710
操作系統:CentOS release 5.7 x86_64版
內核版本:2.6.18
gcc版本:4.1.2
g++版本:4.1.2
java版本:1.6.0_30
gcc、g++和java是必須的,如果運行上述命令提示command not found,則需要安裝。具體安裝方法這裏不做介紹,請參閱相關文檔。
接下來要準備的是apache服務器、tomcat服務器和JK連接器
1.下載apache服務器源碼包
apache服務器官方沒有發佈編譯好的linux二進制包,只能通過下載源代碼,然後自己編譯。因此需要先下載源碼。
訪問網址http://httpd.apache.org/download.cgi,可以看到apache服務器目前放出的版本信息,推薦使用穩定版的release。
然後選擇Unix版源碼:
2.下載tomcat服務器源碼包
目前tomcat服務器個人還是覺得6.0比較穩定。7.0畢竟是新出的東西,需要一定的生產實踐考驗才能達到理想的狀態。因此這裏選擇tomcat 6.0。
訪問網址http://tomcat.apache.org/download-60.cgi,可以看到目前穩定的版本爲6.0.33:
這裏強烈建議下載tar.gz格式的壓縮包。在Linux下,文件訪問有着嚴格的權限限制。一個文件是否允許以二進制或者腳本的形式執行,完全取決於其是否擁有執行缺陷,這與Windows識別文件後綴名(.exe、.bat)的方式不同。zip格式的壓縮包中是不保留文件的權限信息的,而tar.gz格式的壓縮包是保存有文件的權限信息的。
3.下載JK連接器源碼包
作爲apache與tomcat連接的橋樑,JK連接器使用C語言編寫,與apache緊密結合,作爲模塊裝載到apache服務器中,通過配置實現與特定的tomcat服務器進行通信,從而實現負載均衡的功能。
訪問網址http://tomcat.apache.org/download-connectors.cgi,可以找到最新最穩定的JK連接器版本:
這裏還是推薦下載tar.gz格式的源碼。原因同上。
4.解壓
apache服務器、tomcat服務器和JK連接器都已經下載好了,如下圖所示:
然後將這三個包都解壓出來:
5.編譯apache服務器
首先編譯apache服務器。在編譯之前需要執行其自帶的檢測配置腳本。對於不同發行版本的Linux,默認安裝的庫都有所差別,即便是同一個發行版本,由於用戶安裝軟件的軟件不同,也會導致系統內包含的庫有所區別。因此apache作爲開源服務器,在編譯前需要了解系統的庫安裝情況,某些模塊需要依賴於特定的庫,如果這些庫不存在,配置腳本將自動忽略這些庫的編譯。經過檢測時候會生成合適的MakeFile文件。這裏特別提醒一句,如果直接執行配置腳本,是不會編譯額外的模塊的,我們希望使用額外模塊時,需要在運行配置腳本命令後加入參數,讓其盡最大可能編譯可用的庫。關於這方面的介紹可以參閱我的另外一篇文章“Linux下編譯apache服務器modules文件夾缺少模塊(.so)的問題”(http://blog.csdn.net/chaijunkun/article/details/6977466)。下面進入apache服務器源碼目錄並執行配置腳本:
加入--with-mpm=worker是修改apache服務器的工作模式。默認模式是prefork。prefork採用預派生子進程方式,用單獨的子進程來處理 不同的請求,進程之間彼此獨立。相對於prefork,worker是全新的支持多線程和多進程混合模型的MPM(多路處理模塊)。由於使用線程來處理,所以可以處理相對海量的請求,而系統資源的開銷要小於基於進程的服務器。但是,worker也使用了多進程,每個進程又生成多個線程,以獲得基於進程服務器的穩定性。
如果配置過程中出現
這樣的錯誤信息,說明本機沒有安裝apr運行庫,需要下載並安裝。訪問網址:http://apr.apache.org/download.cgi,下載apr和apr-util:
解壓apr和apr-util
進入apr,並編譯
生成了MakeFile後直接編譯
編譯好之後使用root權限安裝:
然後使用類似的方法配置apr-util:
編譯apr-util:編譯好之後使用root權限安裝:
當然如果你在配置apache服務器編譯的時候沒有提示缺少“APR”,請忽略上面關於APR編譯的幾步。
回到apache服務器源碼所在目錄,開始編譯:
編譯過程大概不到十分鐘,完成之後使用root權限進行安裝
如果不出意外,至此apache就安裝成功了。來測試一下:
進入apache服務器的bin目錄,並啓動服務器:
在本地打開瀏覽器,訪問http://127.0.0.1
如果出現“It Works!”則表示啓動成功了
這裏要注意一點就是Linux的防火牆問題。如果你的Linux服務器啓動了防火牆,本地訪問上面的網址是沒有問題的,但如果其它計算機訪問你的服務器有可能會連接失敗。
出現這種情況的原因是防火牆將入站80端口封鎖了。解決方法是將80端口加入到允許列表中:
進入防火牆設置後,如果發現Firewall狀態爲Enabled,表示防火牆已啓用,需要將WWW(HTTP)服務標記爲信任,如果需要使用hhtps協議,還要將Secure WWW(HTTPS)服務也標記爲信任。如下圖所示:
另外,此時如果有其他程序佔用80端口也是會影響到apache服務器的,需要確保這個端口沒有被佔用。
還有我還要補充一點,在Mac OS中按照上述方法安裝apache服務器是不行的。開始的時候我不想搭建Linux服務器,想到Mac OS也是類Unix的系統,操作命令什麼的都一樣,就先在Mac上實驗了。結果安裝上apache服務器後啓動了,每次訪問都提示505錯誤,service temporarily unavailable。經過查閱很多資料和嘗試才發現,原來Mac系統中已經自帶了apache服務器。具體應用是在“系統設置”中的“共享”功能。這個功能裏有“Web共享”方式。其實現時使用的服務器就是apache。它採用的配置文件在/etc/httpd/目錄中。這裏的配置文件和自己安裝的apache服務器配置文件衝突了,因此造成505錯誤。這一點需要注意。(注:我是用的Mac系統爲Mac OS X Lion 10.7.2)
2011年11月23日補充:
如果你希望把apache服務器註冊爲系統服務,讓它隨着系統啓動而啓動,則需要在/etc/init.d/目錄中建立服務管理腳本,我們將其命名爲httpd:
編寫完成後保存並賦予755權限。然後在該目錄下執行
將服務添加到系統。腳本的具體解釋請參閱我的另外一篇博文:
Linux中將memcached註冊爲系統服務(地址:http://blog.csdn.net/chaijunkun/article/details/7000600)。
6.編譯JK連接器
剛剛完成了apache服務器的編譯,接下來順便把JK連接器也編譯出來。
進入剛剛解壓出來的tomcat-connector目錄,再進入native目錄。執行配置:
這裏需要注意的是配置腳本要添加一個apxs完整路徑作爲參數。apxs是一個爲Apache HTTP服務器編譯和安裝擴展模塊的工具,用於編譯一個或多個源程序或目標代碼文件爲動態共享對象,使之可以用由mod_so提供的LoadModule指令在運行時加載到Apache服務器中。
另外,配置腳本運行時會檢查g++所在的目錄,如果沒有安裝g++,則會顯示:
請檢查是否已經正確安裝了c++編譯器。
因爲實驗用的服務器安裝的是X86_64版的Red Hat Enterprise Linux Server ,因此要安裝如下的包:
libstdc++-devel-4.1.2-46.el5.x86_64.rpm
gcc-c++-4.1.2-46.el5.x86_64.rpm
如果使用rpm命令無法安裝,可以在http://szmov.net/centos5464/CentOS/裏查找到相應的資源,下載下來安裝也是一樣的。
配置無誤後就可以編譯了,執行make命令:
7.JK連接器模塊的部署
編譯完成後使用ls命令來列出native目錄下的所有目錄和文件。注意有apache-1.3和apache-2.0兩個目錄。由於在配置編譯的時候指定了apxs工具的位置。配置腳本會根據apxs的反饋結果自動識別目標apache服務器爲2.x版本,因此本次編譯生成的mod_jk.so模塊會放在apache-2.0目錄中,apache-1.3目錄中是沒有mod_jk.so的,這一點請注意。如下所示:
我們現在將編譯好的mod_jk.so拷貝到apache服務器的modules目錄中,這個目錄是專門用來存放擴展模塊的:
至此JK連接器模塊就部署完成了,但是還需要配置,具體配置將在下文中詳細描述。
8.部署tomcat服務器
由於要在本地開啓兩個tomcat服務器實例以模擬負載均衡+羣集的效果,因此我們需要將之前解壓出來的tomcat複製成兩份,進入解壓時的目錄,重命名解壓出來的原始目錄爲tomcat_server_1,然後複製此目錄,副本目錄名稱爲tomcat_server_2:
現在測試tomcat_server_1是否能夠正常工作。
將我實現寫好的一個測試用例下載下來(測試用例基於Spring 3.0編寫,已經打成war包),下載地址:http://download.csdn.net/detail/chaijunkun/3815798。下載得到的文件是TestProject.war。將此壓縮包放入tomcat_server_1的webapps目錄下。然後切換到tomcat_server_1的bin目錄下,啓動tomcat_server_1:
然後在瀏覽器中訪問http://127.0.0.1:8080/TestProject/showInfo.do,如果沒什麼意外會顯示類似於下面的信息:
信息中顯示了當前測試用例所在的路徑以及當前會話的SessionId。
此處要注意的地方同測試apache服務器是否正常工作時是一樣的,需要注意防火牆是否阻塞了tomcat服務器默認採用的8080端口,是否有其他程序佔用此端口。
看到沒什麼問題,我們先吧tomcat_server_1關閉
9.apache服務器的配置
apache服務器、tomcat服務器和JK連接器都部署完成並能正確執行後就可以開始配置了
用vi或者其它編輯器打開/usr/local/apache2/conf/httpd.conf文件(由於該文件權限屬性爲rw-r--r--,因此要想修改此文件需要root權限),這就是apache服務器的主配置文件了。
這裏我推薦使用圖形化的編輯器來編輯它。因爲這個文件很多行,如果用文本模式的編輯器編輯個人感覺很繁瑣。
在有很多LoadModule語句的地方,末尾追加一行
LoadModule jk_module modules/mod_jk.so
然後在寫有<IfModule XXXX>的區域追加一行如下配置
<IfModule jk_module>
JkWorkersFile conf/workers.properties
JkMountFile conf/uriworkermap.properties
JkLogFile logs/mod_jk.log
JkLogLevel warn
</IfModule>
下面給出了一個我寫的配置。注意配置中有註釋的地方。“#”開頭的行爲註釋行。已經去除了原有的配置中的多餘註釋。
LoadModule表示當apache服務啓動時要加載模塊 jk_module爲模塊的別名,後面跟的modules/mod_jk.so就是相對於apache服務器所在目錄(/usr/local/apache2/)的模塊文件名。
<IfModule jk_module>區域表示當apache服務器加載jk_module(在LoadModule指令中指定的模塊別名)模塊時所做的配置。
其中:
JkWorkersFile 指定負載均衡服務器的配置文件,文件名爲相對於apache服務器所在目錄的conf/workers.properties文件
JkMountFile 指定那些請求交由負載均衡服務器來處理,那些由apache服務器來處理,配置文件爲相對於apache服務器所在目錄的conf/uriworkermap.properties文件
JkLogFile 指定JK連接器的日誌輸出文件,文件爲相對於apache服務器所在目錄的logs/mod_jk.log文件
JkLogLevel 指定JK連接器輸出日誌的級別,級別爲warn以上的日誌將被輸出到日誌文件中,可選的值級別由低到高分別爲:TRACE DEBUG INFO WARN ERROR FATAL
------------------------------------------------------------------------------------------------------------------------------------------------
<IfModule worker.c>區域表示當apache服務器以worker模式工作時使用的配置。
指令說明:
StartServers:設置服務器啓動時建立的子進程數量。因爲子進程數量動態的取決於負載的輕重,所有一般沒有必要調整這個參數。
ServerLimit:服務器允許配置的進程數上限。只有在你需要將MaxClients和ThreadsPerChild設置成需要超過默認值16個子進程的時候才需要使用這個指令。不要將該指令的值設置的比MaxClients 和ThreadsPerChild需要的子進程數量高。修改此指令的值必須完全停止服務後再啓動才能生效,以restart方式重啓動將不會生效。
ThreadLimit:設置每個子進程可配置的線程數ThreadsPerChild上限,該指令的值應當和ThreadsPerChild可能達到的最大值保持一致。修改此指令的值必須完全停止服務後再啓動才能生效,以restart方式重啓動將不會生效。
MaxClients:用於伺服客戶端請求的最大接入請求數量(最大線程數)。任何超過MaxClients限制的請求都將進入等候隊列。默認值是"400",16 (ServerLimit)乘以25(ThreadsPerChild)的結果。因此要增加MaxClients的時候,你必須同時增加 ServerLimit的值。筆者建議將初始值設爲(以Mb爲單位的最大物理內存/2),然後根據負載情況進行動態調整。比如一臺4G內存的機器,那麼初始值就是4000/2=2000。
MinSpareThreads:最小空閒線程數,默認值是"75"。這個MPM將基於整個服務器監視空閒線程數。如果服務器中總的空閒線程數太少,子進程將產生新的空閒線程。
MaxSpareThreads:設置最大空閒線程數。默認值是"250"。這個MPM將基於整個服務器監視空閒線程數。如果服務器中總的空閒線程數太多,子進程將殺死多餘的空閒線程。MaxSpareThreads的取值範圍是有限制的。Apache將按照如下限制自動修正你設置的值:worker要求其大於等於 MinSpareThreads加上ThreadsPerChild的和。
ThreadsPerChild:每個子進程建立的線程數。默認值是25。子進程在啓動時建立這些線程後就不再建立新的線程了。每個子進程所擁有的所有線程的總數要足夠大,以便可以處理可能的請求高峯。
MaxRequestsPerChild:設置每個子進程在其生存期內允許伺服的最大請求數量。到達MaxRequestsPerChild的限制後,子進程將會結束。如果MaxRequestsPerChild爲"0",子進程將永遠不會結束。將MaxRequestsPerChild設置成非零值有兩個好處:可以防止(偶然的)內存泄漏無限進行而耗盡內存;
給進程一個有限壽命,從而有助於當服務器負載減輕的時候減少活動進程的數量。
如果設置爲非零值,筆者建議設爲10000-30000之間的一個值。
公式:
ThreadLimit >= ThreadsPerChild
MaxClients <= ServerLimit * ThreadsPerChild,並且MaxClients必須是ThreadsPerChild的倍數
MaxSpareThreads >= MinSpareThreads+ThreadsPerChild
------------------------------------------------------------------------------------------------------------------------------------------------
接下來配置上面提到的conf/workers.properties文件和conf/uriworkermap.properties文件:
進入apache服務器的conf目錄
建立workers.properties和uriworkermap.properties文件
下面給出我已經配置好的兩個文件
worker.list 首先配置了兩個worker,一個用於負載均衡,一個用於監視負載均衡狀態。別名分別爲loadBalanceServers和jk_watcher
然後分別配置位於本機的兩個負載均衡服務器
worker.s1.port:第一臺負載均衡服務器AJP協議連接器的連接端口,這裏配置爲8109
worker.s1.host:第一臺負載均衡服務器的主機名、域名或者IP地址,這裏配置爲本機localhost
worker.s1.type:JK模塊實現負載均衡採用的是AJP協議1.3版本,因此第一臺負載均衡服務器的類型配置爲ajp13
worker.s1.lbfactor:第一臺負載均衡服務器在整個負載均衡系統中所佔的權重,這裏配置爲10,權重越大,越有可能處理更多的請求,建議給性能好的機器配置更高的權重。
worker.s1.cachesize:apache服務器是多線程的,tomcat能夠利用這一優勢來維持一定數量的連接作爲緩存。根據用戶的多少來配置一個合適緩存連接數量有助於提高性能。這裏配置爲5
2013年7月8日補充:最近配置的這臺單擊集羣出現了問題,在高併發量的情況下經常會報HTTP 503錯誤,這裏我在每個worker上配置瞭如下參數:
同樣的配置也爲s2增加了一份。這樣JK組件和tomcat之間的連接池數量就增加了。另外爲了應付大併發量下linux文件句柄不夠用的情況,還需要配置ulimit -n
我這裏配置的是65535。
s1是第一臺負載均衡服務器的別名,這個別名要牢記,因爲在接下來的配置中還會用到。
s2作爲第二臺負載均衡服務器,配置與s1大致相同。區別是AJP協議連接器的連接端口與s1的不同,這是因爲要在同一臺物理機上部署兩個tomcat服務器的緣故。如果是兩臺物理機,則可以配置相同的端口,那麼host屬性就應該不一樣了。兩個tomcat服務器的權重都是10,則兩個tomcat服務器將會有相同的處理請求的機會。
worker.loadBalanceServers.type:設置名稱爲“loadBalanceServers”的worker類型,這裏配置爲lb,也就是Load Balance負載均衡
worker.loadBalanceServers.balanced_workers:設置名稱爲“loadBalanceServers”的worker擁有哪些負責負載均衡的服務器實例,這裏配置爲s1和s2
worker.loadBalanceServers.sticky_session:設置負載均衡是否採用粘性會話。如果該屬性設置爲true,假設一個請求被s1處理了,下次來源於同一個客戶端的請求也將被s1處理。直到s1已經達到最大連接數,JK纔會將會話切換到其他服務器上。但是如果恰巧一直負責處理該會話的服務器down掉了,則會話將會丟失,明顯的故障現象就是關於session的操作會出現莫名其妙的錯誤(例如你所運行的應用中用戶可能已經登錄了,但突然在一次訪問後莫名其妙地提示沒有登錄)。這裏配置爲false,不啓用粘性會話,讓服務器都有機會處理請求,提高了系統的穩定性。
worker.jk_watcher.type:設置名稱爲“jk_watcher”的worker類型,這裏配置爲status,用於監視各個負載均衡服務器實例的運行狀態
# worker.jk_watcher.read_only:設置名稱爲“jk_watcher”的worker是否爲只讀。上面已經將這個worker設置爲了監控worker,如果設置爲只讀,就不能對負載均衡服務器參數進行配置了,這裏先將這條配置註釋掉,默認值爲false,表示可以配置參數。
worker.jk_watcher.mount:設置名稱爲“jk_watcher”的worker(負載均衡服務器實例監視器)的掛載路徑,這裏配置爲/admin/jk。這樣就可以通過http://127.0.0.1/admin/jk來訪問監視工具了,可以很方便地看到各個負載均衡服務器的工作情況。
worker.retries:這是worker全局的重試次數。在apache服務器啓動後,會最多嘗試若干次去連接這些負載均衡服務器,若連接不上就認爲是down掉了,這裏配置爲3
下面給出配置,其作用是告訴apache服務器哪些請求由負載均衡服務器處理:
在配置文件中,以“!”開頭的條件表示“不要”,“=”表示交給。
因此條件“/*=loadBalanceServers”表示將任何請求交給負載均衡服務器。
條件“!/*.jpg=loadBalanceServers”表示不要將.jpg結尾的請求交給負載均衡服務器
apache服務器接收到一個請求後會按照配置文件中的約束條件一個一個地檢查,然後按照最後滿足的匹配條件來決定由哪個worker來處理請求。
我的測試用例中需要輸入http://127.0.0.1/TestProject/showInfo.do來查看信息。那麼接下來就將這個請求作爲示例來解釋上面配置文件的工作過程:
經過上面的條件篩選,最符合條件的就是“/*=loadBalanceServers”。因此將請求轉給了負載均衡服務器。
試想一下,如果在apache主目錄下放置了一個名爲a.jpg的圖片,訪問路徑爲http://127.0.0.1/a.jpg,請求經過該配置的檢查,最後滿足的條件就是“!/*.jpg=loadBalanceServers”,不要將.jpg結尾的請求交給負載均衡服務器,因此apache服務自己處理了該請求。
.jpg是靜態數據,apache由C語言實現,直接針對系統底層進行IO操作,因此靜態性能優良。而tomcat作爲Servlet容器,擅長的是J2EE相關業務的解析。因此通過這樣配置可以實現應用的“動靜態分離”,相互取長補短,優化了性能。類似地也可以將.js、.css和.html等等靜態文件按照上述格式填寫到uriworkermap.properties配置文件中。
10.tomcat服務器的配置
由於在同一臺物理機中部署了兩個tomcat服務器實例,因此需要對端口相關的設置特別小心。tomcat服務器的主配置文件server.xml位於conf目錄內。爲了配置簡單,我將最原始server.xml配置文件中的所有註釋刪除,然後配置好了一個模板,該模板是s1((即tomcat_server_1)的配置文件,如下所示:
配置好s1服務器後再配置s2服務器。按照上面模板中的註釋要求,修改相應的端口就可以了。PS:<Cluster ...></Cluster>節點之間部分不必自己動手敲進去,在tomcat服務器目錄的/webapps/docs/cluster-howto.html文件中有這一段文字,拷貝出來貼到server.xml文件中即可。
2013年7月8日補充:
當大併發量存在時,即便Apache的JK組件與tomcat保持足夠多數量的連接,也不能有效避免HTTP 503錯誤,因爲在大量的請求被轉發到tomcat時,tomcat無法相應那麼多請求,於是拋出了503。需要在Connector的地方配置maxThreads參數,將最大線程數調大。如下所示:
另外,如果業務代碼中有頻繁的SQL操作,還需要增加連接池的最大連接數,並控制好連接空閒釋放連接的時間,因爲如果時間太長,連接得不到釋放,數據庫連接會變得越來越多越來越多,直至連管理都登不上去。因爲這個不在本文討論範圍之內,所以調整數據庫連接池大小請參閱其他文檔。
2012年1月16日補充:
tomcat6中,單機開發時爲了保證GET請求參數採用UTF8編碼(用於支持中文參數),在server.xml中會進行了如下設置:
<Connector port="8080" maxThreads="150" minSpareThreads="25"
maxSpareThreads="75" enableLookups="false" redirectPort="8443"
acceptCount="100" debug="99" connectionTimeout="20000"
disableUploadTimeout="true" URIEncoding="UTF-8" />
但是,當使用apache + tomcat 組成羣集與負載均衡系統時,apache會將servlet/jsp請求轉發給Tomcat。此時是通過AJP協議來轉發的,因此對應的請求實際上是被轉發到Tomcat監聽的AJP端口上的,所以這裏針對8080的設置自然就無效了。正確的方法是進行下面的設置:
<Connector port="8009" enableLookups="false" redirectPort="8443"
debug="0" protocol="AJP/1.3" URIEncoding="UTF-8" />
需要在羣集環境中修改默認URL編碼的朋友們在這裏需要注意一下。
11.部署測試用例
將我提供的TestProject.war文件複製到兩個tomcat服務器實例的webapps目錄下,然後分別啓動tomcat服務器,TestProject.war會自動部署上:
這裏需要注意的是兩個tomcat服務實例的配置文件server.xml的訪問權限。我這裏使用的是root賬戶,所以不用太關心,但是如果用非root賬戶,一定要看看當前賬戶是否有server.xml的讀寫權限。如果沒有,則tomcat服務器將不能成功啓動。這種情況下要麼修改server.xml的訪問權限,要麼使用root權限啓動tomcat服務器。
2011年12月8日補充:最近剛剛發現tomcat的關閉腳本shutdown.sh有問題,經常不能完全回收資源。tomcat是基於Java編寫的,當然其運行也就脫離不了java的JVM。當執行完shutdown.sh腳本後,tomcat服務器表面上是關閉了,然而JVM並沒有完全退出,還在清理並回收資源,如果這個時候立即使用startup.sh進行啓動,很容易導致再啓動一個新的JVM實例,如果維護次數增多就會導致系統內存耗盡,我今天就經歷瞭如下的錯誤:
registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped.
這條錯誤發現於logs目錄下的catalina.日期.log文件中。無論再怎麼啓動tomcat都啓動不了。後來重啓了服務器居然可以啓動了,後來同事說shutdown.sh腳本有問題。於是我經過實驗,果然是這樣。因此在這裏奉勸讀者,如果需要重新啓動tomcat服務器,除了先執行shutdown.sh腳本外,還應該運行ps ax | grep java 來看看有沒有殘留的java進程,如果有,使用kill命令將其殺死,這纔是正確的關閉tomcat服務器方法。
tomcat 6.0.25以後引入了內存泄露偵測,對於垃圾回收不能處理的對像,它就會做日誌。
在tomcat的server.xml文件中,如下配置就是用來做內存泄露偵測的
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>;
也幸虧有了這個東西才提醒了我正在運行着多個JVM實例。
開啓了tomcat服務器之後,就可以開啓apache服務器了(注意,順序很重要!一定要先開tomcat)
這時候如果沒問題,在本機瀏覽器訪問http://127.0.0.1會出現如下頁面:
然後輸入網址訪問http://127.0.0.1/TestProject/showInfo.do來看看效果:
然後再來刷新一下看看:
我們看到,訪問URL沒有改變,但是apache把兩次訪問的負載平均分配給了兩個tomcat服務器實例,並且SessionId是相同的。
當再次刷新頁面的時候,請求又再一次交給了s1服務器。這也正好驗證了之前workers.properties文件中關於兩服務器的權重設置。
至此,apache服務器+tomcat服務器+JK連接器實現負載均衡與羣集的操作結束。
2012年7月2日補充:
有網友留言詢問apache的httpd.conf文件是否要嚴格按照文中我的示例來寫,另外還提出了關於session方面的疑問,我在這裏一併寫上。
首先關於httpd.conf配置文件,我提供的只是一個範本,其中一些用不到的選項是可以去掉的,比方說模塊加載部分。如果有些模塊根本用不到,那去除了之後會使得apache工作得更高效。具體模塊對應的功能還要再參看其他資料。至於session的問題,其實涉及到開發技巧了。既然使用負載均衡和動靜態分離,一般都是按照大網站的架構了,因此對於會話狀態的管理也就需要做出變化。我建議通過加入memcache來實現。用戶登錄之後,服務器端生成唯一識別號(建議再用XXTEA算法加密一下)作爲用戶的Token和memcache裏面的key,然後把用戶對象作爲value存放到memcache中,利用memcache自有的失效機制來實現session功能,再加上memcache本身支持集羣,多少session都能管理得過來。凡是用到session的地方就改成憑藉用戶Token從memcache中取出用戶對象(這套東西還是寫成Util類比較好)。至於用戶Token你怎麼得到,隨你喜好。可以寫在Cookie中,也可以寫在頁面的每一個鏈接中(你可以看一下網易163、126的郵箱,登進去看訪問地址就明白了),後者這樣做的好處是瀏覽器可以禁用Cookie,對於這樣的用戶,在每一個URL中嵌入token=XXXXXX這樣的參數就不會出現問題。
參考文獻:
httpd.conf (4) 詳解,http://hi.baidu.com/prince_zyb/blog/item/685b0901939db70f1c958307.html
Pro Apache Tomcat 6, Matthew Moodie, Kunal Mittal著,Apress公司出品。
ISBN-13 (pbk): 978-1-59059-785-9
ISBN-10 (pbk): 1-59059-785-0