1. 程式人生 > 實用技巧 >5,Tomcat 的負載均衡---Tomcat + httpd(mod_jk)

5,Tomcat 的負載均衡---Tomcat + httpd(mod_jk)

4. Tomcat + httpd(mod_jk)

使用mod_jk模組和tomcat連線時,tomcat的聯結器一般都使用ajp協議型別。

mod_jk不是apache httpd的原生模組,而是類似於第三方模組,因此需要額外編譯mod_jk模組到httpd中,就像將php模組新增到httpd中一樣。

4.1 編譯mod_jk模組

當前最新穩定版的mod_jk是1.2.42版本。

mod_jk下載地址:http://tomcat.apache.org/download-connectors.cgi
mod_jk官方手冊:http://tomcat.apache.org/connectors-doc/

httpd要擴充套件模組需要藉助apxs,它是httpd的開發包httpd-devel中工具,所以先要安裝httpd-devel。如果是編譯安裝的httpd,則devel包已經裝好,如果是yum安裝,則需要額外安裝httpd-devel包。

此處為了方便,httpd使用yum安裝。所以編譯mod_jk的方式如下:

yum -y install httpd httpd-devel
tar xf tomcat-connectors-1.2.42-src.tar.gz
cd tomcat-connectors-1.2.42-src/native/
./configure --with-apxs=/usr/bin/apxs --prefix=/usr/local/tomcat/mod_jk
make && make install

4.2 配置httpd與tomcat的ajp連線

此處暫先配置httpd與其中一個tomcat(192.168.100.22)連線。後文在說明負載均衡時再引入另一個tomcat。

先提供一個額外的httpd配置檔案。

[root@xuexi ~]# cat /etc/httpd/conf.d/mod_jk.conf
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/conf.d/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel debug
######### "JkMount /* TomcatA" will send all request to TomcatA ########
JkMount /*.jsp TomcatA
JkMount /status/* statA
JkUnMount /images/* TomcatA
JkUnMount /css/*.* TomcatA
JkUnMount /css_js/* TomcatA
JkUnMount /*.html TomcatA
JkUnMount /*.js TomcatA

mod_jk的配置檔案官方手冊:http://tomcat.apache.org/connectors-doc/reference/apache.html。以下是幾個常用的指令說明。

  • LoadModule指令用於裝載mod_jk相關模組,除此之外還需要在httpd的配置檔案中設定其它一些指令來配置其工作屬性。如:
  • JkWorkersFile用於指定儲存了worker相關工作屬性定義(見下文)的配置檔案。
  • JkLogFile用於指定mod_jk模組的日誌檔案。
  • JkLogLevel用於指定日誌級別(info,error,debug),此外還可以使用JkRequestLogFormat自定義日誌資訊格式。
  • JkMount(格式:JkMount )則用於控制URL與Tomcat workers的對應關係。可以理解為轉發請求的意思,例如"/status/*"表示url地址後加上/status/可轉發至statA這個worker上。注意,JkMount匹配的URL是相對的。如果JkMount指令放在Location指令中,如<Location /app>,則JkMount將從/app的後面開始匹配。

JkMountJkUnMount是很重要的指令,mod_jk效能之所以比mod_proxy好,就是因為通過這兩個指令可以實現動靜分離,使得只將動態請求轉發給tomcat。其中JkMount指定要轉發給tomcat處理的請求,JkUnMount指定明確不轉發給tomcat而是在本地處理的請求雖然不指定JkUnMount時,也表示不轉發給tomcat,但如果有重疊時,則應該指定JkUnMount。例如下面的例子,除了/myapp/下的js檔案,其他都轉發給tomcat1處理。

JkMount /myapp/* tomcat1
JkUnMount /myapp/*.js tomcat1

對於apache來說,每一個後端Tomcat例項中的engine都可以視作一個worker,而每一個worker的地址、Connector的埠等資訊都需要在apache端指定以便可以識別並使用這些worker。配置這些資訊的檔案通常為"workers.properties",其具體路徑是使用前面介紹過的JkWorkersFile指定的。在apache啟動時,mod_jk會掃描此檔案獲取每一個worker配置資訊。如這裡使用/etc/httpd/conf.d/workers.properties

workers.properties檔案一般由兩類指令組成:一是mod_jk可以連線的各worker名稱列表,二是每一個worker的屬性配置資訊。詳細的配置方法見官方手冊:http://tomcat.apache.org/connectors-doc/reference/workers.html

以下是和上述/etc/httpd/conf.d/mod_jk.conf中配置相對應的/etc/httpd/conf.d/workers.properties。

[root@xuexi tomcat]# cat /etc/httpd/conf.d/workers.properties
worker.list=TomcatA,statA
worker.TomcatA.type=ajp13
worker.TomcatA.host=192.168.100.22
worker.TomcatA.port=8009
worker.TomcatA.lbfactor=1
worker.statA.type = status

關於worker的配置,它們分別遵循如下使用語法。

worker.list = <a comma separated list of worker_name>
worker.<worker_name>.<property>=<property value>

其中worker.list指令可以重複指定多次。worker_name是Tomcat中engine元件中jvmRoute屬性的值(jvmRoute可以不指定,此時worker_name僅用於標識worker)。

根據工作機制的不同,worker有多種不同的型別,每個worker都需要指定其型別,即設定woker..type項。常見的型別如下:其中ajp13是預設值。

  • ajp13:此型別是web server和tomcat首選的型別。此外,還有ajp12和ajp14,但它們一個廢棄一個處於測試階段。
  • lb:lb用於負載均衡場景中的worker;此worker並不真正負責處理使用者請求,而是將使用者請求排程給其它型別為ajp13的worker。
  • status:使用者顯示負載均衡中各worker工作狀態的特殊worker,它不處理任何請求,也不關聯到任何實際工作的tomcat例項。

由於status是狀態監控頁面,所以應該保證其安全性,可以在httpd的配置檔案中加入以下控制列表:

# 注意,必須加上尾隨斜線,因為在mod_jk.conf中已經明確了"/status/*"
# For http 2.2
<Location /status/>
 Order deny,allow
 Deny from all
 Allow from 192.168.100.0/24
</Location>
# For http 2.4
<Location /status/>
 Requrie ip 192.168.100
</Location>

除了type屬性外,worker其它常見的屬性有:

除了type屬性外,worker其它常見的屬性有:

  • hostworker所在的主機,更具體的是tomcatconnector元件設定的ajp監聽地址;
  • portworkerAJP1.3聯結器監聽的埠;
  • connection_pool_minsize:最少要儲存在連線池中的連線的個數;預設為pool_size/2;
  • connection_pool_timeout:連線池中連線的超時時長;
  • mount:由當前worker提供的context路徑,如果有多個則使用空格格開;可考慮在httpd端使用JkMount替代。
  • retries:錯誤發生時的重試次數;
  • socket_timeout:mod_jk等待worker響應的時長,預設為0,即無限等待;
  • socket_keepalive:是否啟用keep alive的功能,1表示啟用,0表示禁用;
  • lbfactorworker的權重,可以在負載均衡的應用場景中為worker定義此屬性;

另外,在負載均衡模式中專用的屬性還有:

  • balance_workers:用於負載均衡模式中的各worker的名稱列表,需要注意的是,出現在此處的worker名稱一定不能在任何worker.list屬性列表中定義過,並且worker.list屬性中定義的worker名字必須包含負載均衡worker
  • method:可以設定為R、T或B;預設為R,即根據請求的個數進行排程(wrr);T表示根據已經發送給worker的實際流量大小進行排程;B表示根據實際負載情況進行排程(leastconn)。
  • sticky_session:將某請求排程至某worker後,此地址後續所有請求都將直接排程至此worker,實現將使用者session與某worker繫結。預設為值為true,即啟用此功能。如果後端的各worker之間支援session複製,則可設為false。

至此,一個基於mod_jk模組與後端名為TomcatA的worker通訊的配置已經完成,重啟httpd服務即可生效。

測試:在瀏覽器中輸入

http://192.168.100.17/
http://192.168.100.17/index.jsp
http://192.168.100.17/status/

如果都能獲取頁面,則表示apache通過mod_jk和tomcat基於ajp協議型別的連線已經成功。

4.3 通過mod_jk負載均衡tomcat

使用mod_jk實現tomcat的負載均衡有一個好處,tomcat上可以禁用http協議(將監聽此協議的Connector配置刪除即可),防止外界直接通過http請求tomcat。

配置apache,使其支援負載均衡,修改/etc/httpd/conf.d/mod_jk.conf為如下內容:

LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/conf.d/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel notice
JkMount /*.jsp TomcatLB
JkMount /status/* statA

編輯/etc/httpd/conf.d/workers.properties,修改為如下內容:為測試負載效果,不啟用stick_session。

worker.list=TomcatLB,statA
worker.statA.type=status
worker.TomcatLB.type=lb
worker.TomcatLB.sticky_session=false
worker.TomcatLB.balance_workers=TomcatA,TomcatB
worker.TomcatA.type=ajp13
worker.TomcatA.host=192.168.100.22
worker.TomcatA.port=8009
worker.TomcatA.lbfactor=5
worker.TomcatB.type=ajp13
worker.TomcatB.host=192.168.100.23
worker.TomcatB.port=8009
worker.TomcatB.lbfactor=10

在mod_jk負載均衡中,後端tomcat的engine元件需要新增jvmRoute引數,該引數會為當前server例項設定全域性惟一識別符號,因此每一個例項的jvmRoute的值均不能相同,且jvmRoute的值必須等於balance_workers的成員值。對於上面的配置,Engine應該如下設定:此處還修改了name,但這不是要求要修改的。

<!-- tomcatA上設定 -->
<Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatA">
<!-- tomcatB上設定 -->
<Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatB">

為了演示效果,在TomcatA部署一個應用程式test。

[root@xuexi tomcat]# mkdir -p /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}

新增index.jsp,內容如下:

[root@xuexi tomcat]# cat /usr/local/tomcat/webapps/test/index.jsp
<%@ page language="java" %>
<html>
 <head><title>TomcatA</title></head>
 <body>
 <h1><font color="red">TomcatA </font></h1>
 <table align="centre" border="1">
 <tr>
 <td>Session ID</td>
 <% session.setAttribute("abc","abc"); %>
 <td><%= session.getId() %></td>
 </tr>
 <tr>
 <td>Created on</td>
 <td><%= session.getCreationTime() %></td>
 </tr>
 </table>
 </body>
</html>

在TomcatB同樣也部署一個應用程式test。如下:

[root@xuexi tomcat]# mkdir -p /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
[root@xuexi tomcat]# cat /usr/local/tomcat/webapps/test/index.jsp
<%@ page language="java" %>
<html>
 <head><title>TomcatB</title></head>
 <body>
 <h1><font color="blue">TomcatB </font></h1>
 <table align="centre" border="1">
 <tr>
 <td>Session ID</td>
 <% session.setAttribute("abc","abc"); %>
 <td><%= session.getId() %></td>
 </tr>
 <tr>
 <td>Created on</td>
 <td><%= session.getCreationTime() %></td>
 </tr>
 </table>
 </body>
</html>

重啟httpd、tomcatA、tomcatB對應的服務程式。在瀏覽器中輸入192.168.100.17/test/index.jsp測試負載均衡是否生效。

測試時,輪調兩次tomcatB後輪調一次tomcatA。而且可以發現每次輪詢時Session ID每次都是變化的,因為沒有開啟sticky_session,所以session沒有進行繫結。

要繫結會話,將worker.properties中的sticky_session設定為true即可。