apache+Tomcat負載平衡設置詳解(轉)


一、簡介:

每個Tomcat worker是一個服務於web server、等待執行servlet的Tomcat實例。例如我們經常使用像Apache之類的web server轉發sevlet請求給位於其後面的一個Tomcat進程(也就是前面所說的worker)。本文詳細介紹瞭如何配置各種類型worker和 loadbalance,並說明了各種類型worker的特性和loadbalance配置的原理。

二、爲什麼使用Tomcat workers:

上文描述了一個非常簡單的結構,事實上能夠配置多個Tomcat workers來處理web server轉發的servlet請求。而這樣配置的理由不外乎以下幾種假想環境:
* 我們在開發環境中發佈不同的Tomcat workers爲各自不同的應用服務。當然在開發環境中的開發者共享同一個web server,但是每個Tomcat worke服務於擁有它的開發者。
* 我們在不同的Tomcat進程上定義各自的虛擬主機,這樣不同的公司可以使用各自的web site,從而使他們的web site得到了合理的分割。
* 我們提供負載平衡的web site,也就意味着同時使用多個Tomcat workers,而每個Tomcat worker具有獨立的主機並且在workers之間要分配通過web server轉發來的請求。
當然,這些假想情況也許並不能涵蓋使用多個workers的所有狀況。

三、workers.properties配置說明:

定義Tomcat workers的方法是在apache的conf目錄下編寫一個名爲“workers.properties”的屬性文件。本文將詳細解釋如何進行配置的:

1.定義Workers列表:
定義workers的方法就是在apache的conf目錄下編寫一個workers.properties文件,使其作爲apache的插件來發揮作用。
定義workers列表的格式:
worker.list =<使用“,”分割的worker 名字列表>
例如:

worker.list= worker1, worker2

當apache啓動時,workers.properties作爲插件將初始化出現在worker.list列表中的workers。

2.定義Workers的類型:

每個被命名的worker都應有一些關於其自身的附加信息。這些信息包括了worker的類型和其它相關信息。這裏討論的是JK1.2.5中定義的workers類型。
定義worker類型的格式:
worker . worker名字. type =<worker類型>
worker名字的命名最好遵循java的命名規範。
worker類型取值於下面的表格:
定義一個名爲“local”的worker,其使用ajpv12協議與Tomcat 進程通訊:

worker.local.type=ajp12

定義一個名爲“remote”的worker,其使用ajpv13協議與Tomcat 進程通訊:

worker.remote.type=ajp13

定義一個名爲“fast”的worker,其使用JNI的方式與Tomcat 進程通訊:

worker.fast.type=jni

定義一個名爲“loadbalancer”的worker,其作爲對多個Tomcat 進程的負載平衡使用:

worker.loadbalancer.type=lb

各個類型具有不同的行爲,我們在下文中會詳細解釋。

3.設置Worker屬性:

在定義worker之後,還需要提供各個worker的屬性,這些屬性的定義使用下面的方式:
worker.<worker名字>.<屬性>=<屬性值>

3-1 ajp12類型的Worker屬性:.

ajp12類型的worker工作時使用基於TCP/IP socket的ajpv12協議轉發請求給“進程外”Tomcat worker。
ajp12 worker屬性如下:
host:
偵聽ajp12請求的Tomcat worker主機。
port:
Tomcat worker主機的偵聽端口。
lbfactor:
當此Tomcat worker被用於一個負載平衡worker使用時,此屬性將被使用。它定義了此worker的負載平衡權值。
例如:下面的“worker1”定義了一個位於www.x.com主機上的Tomcat,它使用8007端口偵聽apache發來的請求,並具有2.5的負載權值

worker.worker1.host=www.x.com
worker.worker1.port=8007
worker.worker1.lbfactor=2.5

注意:在ajpv12協議中,針對每個請求都要一個連接建立、使用、關閉。其默認偵聽端口爲8007。

3-2 ajp13類型的Worker屬性:

ajp13類型的worker工作時使用基於TCP/IP socket的ajpv13協議轉發請求給“進程外”Tomcat worker。
ajpv13協議與ajpv12協議的主要不同:
* ajpv13具有更豐富的二進制協議,它使用將頻繁使用的字符串編碼爲小整數的方式對請求數據進行壓縮。
* ajpv13重用打開的socket並保留這些打開的socket以處理將來的請求。這在apache與Tomcat之間具有防火牆的網絡環境下是必要的。
* ajpv13具有對SSL信息的處理能力,以致容器能夠實現SSL的相關方法(如isSecure())。


注意:ajp13當前只能用於支持“進程外”協議的Tomcat 4.0.x, 4.1.x and 5。
下表描述了ajp13worker接受的屬性:

host:
偵聽ajp13請求的Tomcat worker主機。

port:
Tomcat worker主機的偵聽端口。

lbfactor:
當此Tomcat worker被用於一個負載平衡worker使用時,此屬性將被使用。它定義了此worker的負載平衡權值。

cachesize:
當在多線程的web server(例如apache2.0、IIS 、Netscape)中使用JK時,此屬性是有效的。如果將cachesize的值設置爲較高的值,這些支持多線程的web server將獲得很好的處理能力。如果此屬性不被設置,則連接cache特性將失效。

cache_timeout:
本屬性用於聲明JK在cache中保留一個打開的socket的時間,它對減少web serer的線程數有所幫助。
使用cache_timeout的原因:
周所周知,一個身背重負的web server(例如apache)建立childs/threads來處理負載,而當負載減少時它將銷燬無用的childs/threads。每個 child在轉發請求給Tomcat時要打開一個ajp13連接,而在Tomcat那一端也將建立一個ajp13線程與之通訊。但是問題出現在一個 ajp13連接建立完成後,child沒有及時的釋放那個ajp13連接,由於web server1將保持它的childs/threads運行已處理高負載,即使childs/threads處理快速的靜態內容,在Tomcat端也將積累很多的無用ajp13線程。

socket_keepalive:
當防火牆位於web server與Tomcat之間時,防火牆將嘗試斷開未激活的網絡連接。此屬性將告訴操作系統在未激活的連接中發送KEEP_ALIVE信息(發送間隔時間依賴於操作系統的設置,一般爲120秒),這樣將防止防火牆切斷未激活的網絡連接。
但此設置並不是萬能鑰匙,它對於某些防火牆也無能爲力。

socket_timeout:
此屬性說明連接在未激活的狀況下持續多久,web server將主動切斷之。這是一個使Tomcat端的陳舊線程不致過多的好方法,但是也帶來了在下一次請求到來時需要重新打開socket的開銷。此屬性與cache_timeout有類似的功效,但是它工作在non-cache模式。

connect_timeout:
web server在連接建立後將一個PING請求發送到ajp13協議的連接上。 此屬性說明了web server等待PONG迴應的時間(以ms爲單位)。此屬性在jk 1.2.6版本被增加進來,以求避免Tomcat的死機,Tomcat 3.3.2+, 4.1.28+ and 5.0.13+實現了對使用ajp13的 ping/pong的支持。此屬性默認爲失效的。

prepost_timeout:
web server在轉發一個請求後將一個PING請求發送到ajp13協議的連接上。此屬性說明了web server等待PONG迴應的時間(以ms爲單位)。此屬性在jk 1.2.6版本被增加進來,以求避免Tomcat的死機,Tomcat 3.3.2+, 4.1.28+ and 5.0.13+實現了對使用ajp13的 ping/pong的支持。此屬性默認爲失效的。

reply_timeout:
此屬性告訴web server在接到遠端的Tomcat已死並實時的切換到集羣中的另外一個Tomcat的迴應之前等待一段時間。默認情況下web server將永遠等待。屬性值爲web server要等待迴應的時間(以ms爲單位),所以如果具有運行時間較長的servlet時設置其值要小心。此屬性在jk 1.2.6版本被增加進來,以求避免Tomcat的死機和在支持ajp13的servlet引擎上發生的問題。此屬性默認爲失效的。

recovery_options:
此屬性說明了web server在檢測到Tomcat失敗後如何進行恢復工作。默認情況下,web server將轉發請求給處於負載平衡模式中的另一個Tomcat。屬性值爲0,說明全部恢復;屬性值爲1,說明如果在Tomcat接到請求後出現失敗狀況,則不進行恢復;屬性值爲2,說明如果在Tomcat發送http頭給客戶端後出現失敗狀況,則不進行恢復;屬性值爲3,說明如果在Tomcat接到請求後出現失敗狀況或者在Tomcat發送http頭給客戶端後出現失敗狀況,則不進行恢復。此屬性在jk 1.2.6版本被增加進來,以求避免Tomcat的死機和在支持ajp13的servlet引擎上發生的問題。此屬性默認爲全部恢復。

例如:一個名爲“worker2”的worker的配置:

worker.worker2.host=www2.x.com
worker.worker2.port=8009
worker.worker2.lbfactor=3.5
worker.worker2.cachesize=10
worker.worker2.cache_timeout=600
worker.worker2.socket_keepalive=1
worker "worker2" want ajp13 connection to be dropped after 5mn (timeout)
worker.worker2.socket_timeout=300


說明:上例中的worker要求操作系統在連接上發送KEEP-ALIVE信號。
注意:在ajpv13協議中默認端口爲8009。

4.設置lb Worker屬性:
負載平衡類型的worker並不與Tomcat worker通訊,它負責管理這些Tomcat worker。

其管理範圍如下:
* 初始化在web server的worker列表中定義的worker。
* 使用worker的負載平衡權值,執行基於權值的負載平衡,將數量多的請求發送到負載平衡權值高(在web server看來就是更加健壯的)的worker。
* 維護在同一個Tomcat worker上的同一個session的請求,使其發送到同一個Tomcat worker上。以達到Tomcat worker上的session一致性、持續性。
* 標識已經失敗的Tomcat workers,懸空發向它們的請求,在被lb worker管理的其它workers上尋找可以失敗恢復的worker。
被同一個lb worker管理多個worker之間的負載平衡的(基於它們的lbfactor和當前用戶session),也可以儘量避免由於單一的Tomcat進程死掉而造成這個網站被“殺”的不良反應。
下表說明了lb worker接受的屬性:
* balanced_workers:一個由“,”分割的worker列表,用來聲明lb worker需要被管理的workers。這些workers不應出現在worker.list屬性中。
* sticky_session:表述是否將對SESSION ID的請求路由回到相同的Tomcat worker。如果屬性值不爲0,它將被設置爲JK_TRUE,session將是粘性的,即SESSION ID的請求路由回到相同的Tomcat worker;當Tomcat正使用能夠跨越多個Tomcat實例持久化session數據的Session Manager時,它將被設置爲JK_FALSE。屬性默認值爲JK_TRUE。
例如:worker balance1管理着兩個workers:worker1、worker2:

worker.balance1.balanced_workers=worker1, worker2


5.高級lb Worker屬性:

JK 1.2.x版本通過增加兩個新的屬性:local_worker_only 和 local_worker 爲lb worker增添了新的負載平衡和容錯支持。
下面讓我們舉一個實際的環境作爲example:
一個集羣具有兩個節點(worker1+worker2),一個web server與tomcat workers一前一後,一個負載平衡器(lb Worker)位於節點的前面、web server的後面。
配置如下:

worker.list=router
# Define a 'local_worker' worker using ajp13
worker.worker1.port=8009
worker.worker1.host=node1.domain.org
worker.worker1.type=ajp13
worker.worker1.lbfactor=1
worker.worker1.local_worker=1
# Define another 'local_worker' worker using ajp13
worker.worker2.port=8009
worker.worker2.host=node2.domain.org
worker.worker2.type=ajp13
worker.worker2.lbfactor=1
worker.worker2.local_worker=0
# Define the LB worker
worker.router.type=lb
worker.router.balanced_workers=worker1,worker2
worker.router.local_worker_only=1


在worker1和worker2上的local_worker標誌告訴lb_worker哪個連接屬於本地worker。

如果local_worker值爲非0,則它將被設置爲JK_TRUE,用來標記“local worker”,而JK_FALSE的情況則相反。如果至少一個worker被標記爲local worker,則lb_worker將工作於local worker模式。這種模式下,所有的local workers將被移到在lb_worker中的內部worker列表的頭部。

這意味着一個帶有session id的請求到達lb_worker時,相應的worker(根據權值排序,權值最大的那個worker)將被確定作爲此請求的接受/處理者。如果這個 worker死掉/當機,請求將被髮送到處於非錯誤狀態的第一個local worker;如果一個沒有session id的請求到達lb_worker時,此請求將被路由到第一個local worker。如果所有的local worker均處於錯誤狀態,則這時“local_worker_only”標誌顯得尤其重要。如果local_worker_only的屬性值爲非0,則它被設置爲 JK_TRUE,否則被設置爲 JK_FALSE。當它被設置爲 JK_TRUE時,這個沒有session id的請求將得到一個錯誤作爲迴應,否則lb_worker將嘗試將請求路由到其它的被管理的worker上。如果其中的一個worker處於錯誤狀態,並且恢復會話的工作並沒有任何改變,local worker將查找這個沒有session id的請求(因爲在local worker中保存有這個請求的session),而其它的worker只能查找帶有session id的請求。

注意:local_worker默認值是0,local_worker_only默認值也是0。

6.爲什麼需要這麼複雜的過程嗎?

因爲我們對於一個關閉的節點需要一個具有靈性的維護。
在節點前面的平衡器週期性的對每個節點的特定端口進行查詢。如果我們從集羣中移走一個節點,我們就會隱性的關閉掉這個特定的端口。由於負載平衡器不能連接它,這個節點將被標記爲down。但是我們沒有移動在那個關閉的節點上的session到其它的節點上。在這個環境下,如果平衡器發送一個沒有 session id的請求到一個端口被關掉的節點,那麼一個錯誤將發生。如果平衡器測試到一個節點被標記爲down的狀態,而沒有其它的節點允許發送沒有session id的請求。這樣這些陳舊的session請求就只有路由到那個被關閉的節點才能被接受。在一段時間後,這些陳舊的session將超時。由於所有的陳舊的session過期,那個不可達(被關閉)的節點將失去這個請求。同時也會導致我們的servlet系統發送一個沒有session id的重定向迴應給瀏覽器。

但是可能被關閉的節點將會up,重新加入到集羣中來,在它上面仍將保留着陳舊的session。所以在最後一個session超時後,更新節點能夠爲陳舊的session的恢復帶來希望,而不是殺掉sessions或者把它們移到其它節點上。而且有時如果那些陳舊的session中有許多big的對象,那麼移動它們也將花費許多時間。

7.jni類型的Worker屬性:

jni worker會在web server進程中打開一個JVM,並在其中執行Tomcat,這叫做“進程內”worker。來往於JVM的消息將通過調用JNI方法被傳遞,這使jni worker比那些需要使用ajp消息通訊的“進程外”worker執行的更快。

注意:由於JVM是多線程的,jni worker應該只被用於在支持對線程的web server(AOLServer, IIS, Netscape and Apache 2.0)上。同時還應該確認在web server上使用的線程方案是否與被使用的JK web server插件相匹配。

由於jni worker 打開了一個JVM,它將接受一些屬性(例如classpath等)並將其傳遞給JVM:
worker.worker名.class_path:“進程內”的JVM要使用的classpath。它將包括所有的Tomcat的jar文件和class、配置文件等。

爲了獲得JSP編譯器的支持,我們需要將Javac添加到classpath中。當然對於Java2需要添加tools.jar到classpath,而對於JDK1.xx則要添加classes.zip到classpath。

worker.worker名.class_path:用於以多行的形式聲明多個classpath。JK環境將用“:”或者“;”把這些classpath;連接起來。

例如:給名爲“wrkjni”的worker設置classpath。


worker.wrkjni.class_path=/var/tomcat3/lib/tomcat.jar
worker.wrkjni.class_path=/opt/IBMJava2-131/lib/tools.jar


worker.worker名.bridge:用於標識將通過JNI方式被使用的Tomcat的類型。此屬性目前有兩個屬性值:tomcat32 or tomcat33。Tomcat 3.2.x雖然已經過時,但是被提供用於發佈在一些類似iSeries系統上。此屬性的默認值爲tomcat33。
例如:給“wrkjni”設置bridge類型爲tomcat3.3。


worker.wrkjni.bridge=tomcat33


worker.worker名.cmd_line: 此屬性提供了在Tomcat啓動代碼執行的命令行。使用時將命令行的命令、參數分解爲多個cmd_line屬性。JK環境通過在各個cmd_line屬性值之間添加空格將這些cmd_line連接在一起。
例如:設置“wrkjni”的cmd_line屬性。


worker.wrkjni.cmd_line=-config
worker.wrkjni.cmd_line=/etc/tomcat3/conf/alt-server.xml
worker.wrkjni.cmd_line=-home
worker.wrkjni.cmd_line=/var/tomcat3


上面例子中的第一行聲明瞭-config參數名,而第二行聲明瞭與之對應的參數值。第三行與第四行同理。

worker.worker名.jvm_lib:用於聲明JVM的實現庫的完整路徑。Jni worker使用這個路徑動態裝載JVM。
例如:設置“wrkjni”的JVM shared lib (IBM SDK on Linux)。

worker.wrkjni.jvm_lib=/opt/IBMJava2-131/jre/bin/classic/libjvm.so

例如:設置“wrkjni”的JVM shared lib (Sun SDK on Windows)。

worker.wrkjni.jvm_lib=c:\JDK\1.3.1\jre\bin\classic

worker.worker名.stdout:設置JVM寫它的System.out的完整路徑位置。
例如:將“wrkjni”的JVM系統輸出路徑設置爲/var/log/http/jk-jvm-out.log。

worker.wrkjni.stdout=/var/log/http/jk-jvm-out.log

worker.worker名.stderr:設置JVM寫它的System.err的完整路徑位置。
例如:將“wrkjni”的JVM系統錯誤輸出路徑設置爲/var/log/http/jk-jvm-err.log

worker.wrkjni.stderr=/var/log/http/jk-jvm-out.log

worker.worker名.ms:設置JVM的初始堆大小。
例如:設置“wrkjni”的JVM的初始堆爲64M。

worker.wrkjni.ms=64


worker.worker名.mx:設置JVM的最大的堆大小。
例如:設置“wrkjni”的JVM堆最大爲128M

worker.wrkjni.mx=128


worker.worker名.sysprops:設置JVM的系統屬性。
例如:設置“wrkjni”的JVM使用法語。

worker.wrkjni.sysprops=-Duser.region=FR


worker.worker名.ld_path:設置附加的動態鏈接庫路徑(類似於LD_LIBRARY_PATH)
例如:添加一些動態鏈接庫路徑到“wrkjni”的java環境中。

worker.wrkjni.ld_path=/opt/IBMJava2-131/jre/bin/
worker.wrkjni.ld_path=/opt/IBMJava2-131/jre/bin/classic


注意:在Linux下,上面的ld_path並不能更新LD_LIBRARY_PATH,所以需要在執行web server之前手動更新LD_LIBRARY_PATH,。

8.屬性文件宏:

我們可以在屬性文件中定義“宏”。這些宏讓我們定義屬性,並在以後使用它們來構建其它的屬性文件。當我們修改Java Home、Tomcat Home、系統路徑分隔符時這是很有用的。
例如:定義了屬性workers.tomcat_home、workers.java_home。

workers.tomcat_home=d:\tomcat
workers.java_home=d:\sdk\jdk1.2.2

在定義worker.inprocess.class_path時就可以使用前面定義的workers.tomcat_home。

worker.inprocess.class_path=$(workers.tomcat_home)$(ps)classes



9.一個簡單而完整的worker.properties:

文件中定義了比較完整的結構,可以做爲參考模版:
* 一個位於localhost的使用8007端口的ajp12 worker;
    * 一個位於localhost的使用8008端口的ajp13 worker;
    * 一個jni worker;
    *  一個lb worker:負責ajp12 worker、ajp13 workers的負載平衡。
文件內容如下:

# Define some properties
workers.apache_log=/var/log/httpd/
workers.tomcat_home=/var/tomcat3
workers.java_home=/opt/IBMJava2-131/
ps=/
# Define 4 workers, 3 real workers using ajp12, ajp13, jni, the last one being a loadbalancing worker
worker.list=worker1, worker2, worker3, worker4
# Set properties for worker1 (ajp12)
worker.worker1.type=ajp12
worker.worker1.host=locahost
worker.worker1.port=8007
worker.worker1.lbfactor=5
# Set properties for worker2 (ajp13)
worker.worker2.type=ajp13
worker.worker2.host=locahost
worker.worker2.port=8009
worker.worker2.lbfactor=50
worker.worker2.cachesize=10
worker.worker2.cache_timeout=600
worker.worker2.socket_keepalive=1
worker.worker2.socket_timeout=300
# Set properties for worker3 (jni)
worker.worker3.type=jni
# Set worker3 bridge type, here Tomcat 3.3
worker.worker3.bridge=tomcat33
# Set worker3 classpath
worker.worker3.class_path=$(workers.tomcat_home)$(ps)classes
worker.worker3.class_path=$(workers.tomcat_home)$(ps)lib$(ps)tomcat.jar
# Set worker3 tomcat command line
worker.worker3.cmd_line=-home
worker.worker3.cmd_line=$(workers.tomcat_home)
# Set worker3 Tomcat/JVM settings
worker.worker3.jvm_lib=$(workers.java_home)$(ps)jre$(ps)bin$(ps)classic$(ps)libjvm.so
worker.worker3.stdout=$(workers.apache_log)$(ps)inprocess.stdout
worker.worker3.stderr=$(workers.apache_log)$(ps)inprocess.stderr
worker.worker3.sysprops=tomcat.home=$(workers.tomcat_home)
# Set properties for worker4 (lb) which use worker1 and worker2
worker.worker4.balanced_workers=worker1,worker2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章