Linux下使用Apache的Httpd+Mod_jk+Tomcat搭建Web集群服務
目的
使用多個tomcat服務器來對請求進行分流,防止單個服務器壓力過重。這裡為了簡單,只使用兩個tomcat。
軟件
apache httpd-2.2.31(下載地址:https://httpd.apache.org/download.cgi)
apache tomcat-7.0.69(下載地址:https://tomcat.apache.org/download-70.cgi)
tomcat-connectors-1.2.41(即mod_jk,下載地址:https://tomcat.apache.org/download-connectors.cgi)
軟件都在/home/hey/apache-src 下
單機版(垂直集群)
即apache與幾個tomcat都部署在一個機器上,實際上分布式集群應該是apache部署在主節點上,多個tomcat分別在不同的機器上. 如果tomcat都在一個機器上,那麼使用不同的端口。
建立一個apache目錄 cd /home/hey
mkdir apache
安裝httdp解壓httpd並進入目錄
tar -zxvf httpd-2.2.31.tar.gz
mv httpd-2.2.31 httpd
cd httpd
進行編譯參數配置
./configure –prefix=/home/hey/apache/apache –enable-mods-shared=all
編譯安裝
make
make install
如果過程中報Cannot use an external APR with the bundled APR-util,那麼還需要安裝apr和apr-util,如果httpd是2.4+版本,那麼還需要先下載apr和apr-util,詳細參見:/content/543613.html。
安裝結束,測試啟動
啟動
cd /home/hey/apache/apache/bin/apachectl start
停止
cd /home/hey/apache/apache/bin/apachectl stop
啟動後,你在機器上輸入這台機器的ip,顯示It works 則表明配置成功.
為了直接使用apacheclt命令,配置~/.bash_profile
vi ~/.bash_profile
HTTPD_HOME=/home/hey/apache/apache
PATH=PATH:PATH:HTTPD_HOME/bin
export HTTPD_HOME
安裝tomcat-connectors-1.2.41
解壓源文件
tar -zxvf tomcat-connectors-1.2.41-src.tar.gz
進入native目錄
cd tomcat-connectors-1.2.41-src/native
進行編譯參數配置
./buildconf.sh
./configure –with-apx=/home/hey/apache/apache/bin/apxs \
–with-java-home=$JAVA_HOME –with-java-platform=2\
–enable-jni
編譯安裝
make
make install
是否成功
如果成功,則在httpd安裝目錄下/home/hey/apache/apache/modules可以看到mod_jk.so文件
安裝讀個tomcat(已兩個為例)
解壓tomcat
tar -zxvf apache-tomcat-7.0.69.tar.gz
mv apache-tomcat-7.0.69 tomcat1
cp -r tomcat1 ../apache/tomcat1
cp -r ../apache/tomcat1 ../apache/tomcat2
集群配置
配置tomcat
修改端口
tomcat的配置主要是對tomcat/conf/server.xml文件的配置.
因為,兩個tomcat都部署在一台機器上,即一台機器上運行兩個tomcat的實例,因此,要配置不同的端口,即要對server.xml中配置的所有端口進行修改,避免端口被占用。這裡對所有的端口在默認端口上加上1000。
如下:
修改tomcat1的server.xml的SHUTDOWN端口8005為9005,tomcat2的8005為10005,如下:
[code]<Server port="8005" shutdown="SHUTDOWN">
改為:
[code]<Server port="9005" shutdown="SHUTDOWN">
同理,tomcat2的改為10005。
分別修改tomcat1與tomcat2的HTTP Connector端口,從默認的8080分別改為9080與10080,如下:
[code] <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
改為:
[code] <Connector port="9080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
同理,tomcat2的改為10080。
分別修改tomcat1與tomcat2的AJP Connector端口,從默認的8009分別改為9009與10009,如下:
[code] <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
改為:
[code] <Connector port="9009" protocol="AJP/1.3" redirectPort="8443" />
同理,tomcat2的改為10009。
3. tomcat集群配置
需要再在server.xml文件改動兩處:
為
<Engine>
元素設置jvmRoute屬性 引用[1]:
在server.xml文件中可以找到注釋掉的帶jvmRoute的
<Engine>
配置,如下圖:
參考這個配置就可以了,在為被注釋掉的
<Engine>
加上jvmRoute屬性,如下:
[code] <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
tomcat2的jvmRoute設置為tomcat2.
對於jvmRoute的取值要特別注意:其值必須於後面要提到的mod_jk的workers.properties文件中結點名相一致!由於那個文件中結點名為tomcat1和tomcat2,因此,此處我們必須設定jvmRoute的值為這兩個值之一。關於這一點在tomcat官方關於Apache Tomcat Connector的文檔 http://tomcat.apache.org/connectors-doc/webserver_howto/apache.html 也有明確提及。jvmRoute值會出現在由該結點創建的session id中,例如:在非集群環境下,一個sessionid可能是 “xxxxxxxxx” 的格式,而在集群環境下,如果當前結點的jvmRtomat1oute被配置為tomcat1,那由該結點生成的sessionid將變成“xxxxxxxxx.tomat1”格式,而mod_jk正是依賴於這個節點後綴實現sticky session的,也就是把所有後綴是tomat1的請求都發送給tomat1結點進行處理。
注意:個人在實驗中發現有問題,如果tomcat配置了jvmRoute,並且httpd的conf下的workers.properties(下文會講到)的worker.controller.sticky_session=1的話,會發現多次請求後,都是固定的一台tomcat(或者tomcat1或者tomcat2)在處理,刷新浏覽器窗口總是在某一個tomcat控制台輸出形如(上面的請求都是一台機器上的浏覽器請求,如果ip不變,那麼可以認為是同一個客戶的請求):
要麼一直是:
SessionID:154678FA6D4D0ABD57658B750E7A3532.tomcat1 (在tomcat1窗口)
要麼一直是:
SessionID:3800571A532AECEA7280F45361861AD4.tomcat2 (在tomcat2窗口)
由控制台打印的結果可以看出,SessionID在哪個tomcat上產生,那麼後續該客戶端的請求將總是會這個tomcat來處理。
其實worker.controller.sticky_session=1表示某一 client的session創建之後,後續由該客戶端發起的請求,也就是這個session的所有請求都始終由第一次處理該請求的結點負責處理(除非該結點掛掉),0則相反。
(1) 如果你需要設置相同的客戶端一直在同一個tomcat服務器上(除非這台掛掉樂),那麼需要設置tomcat的jvmRoute與worker.controller.sticky_session=1。
(2) 如果你需要設置相同的客戶端可以隨機分配到不同的tomcat服務器上,那麼需要設置tomcat的jvmRoute與worker.controller.sticky_session=0,或者不設置tomcat的jvmRoute。
具體參見後面的截圖。
在
<Engine>
元素中添加機器配置信息
在tomcat官網中的集群配置文檔:http://tomcat.apache.org/tomcat-6.0-doc/cluster-howto.html 中,有一份默認配置:
[code] <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=""/>
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
tempDir="/tmp/war-temp/"
deployDir="/tmp/war-deploy/"
watchDir="/tmp/war-listen/"
watchEnabled="false"/>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
這個默認配置一般可滿足要求,需要直接將上面的配置復制到
<Engine>
元素中便可,這有tomcat便配置完畢。
4. 配置Apache httpd的配置
mod_jk配置項
編輯在apache httpd安裝目錄的conf目錄下(此處是/home/hey/apache/apache/conf)的httpd.conf文件,在文件最後追加以下內容:
[code]# Load mod_jk module
LoadModule jk_module modules/mod_jk.so
# Specify jk log file.
JkLogFile /var/log/mod_jk.log
# Specify jk log level [debug/error/info]
JkLogLevel info
# Specify workers.properties, this file tell jk:
# how many nodes and where they are.
JkWorkersFile conf/workers.properties
# Specify which requests should handled by which node.
JkMount /* controller
關於mod_jk配置項的詳細內容,可參考:http://tomcat.apache.org/connectors-doc/webserver_howto/apache.html
上述配置中:JkWorkersFile conf/workers.properties 指明由一個workers.properties文件來描述集群結點的情況,因此,我們需要創建這個workers.properties文件,並放置於conf文件夾下。
配置workers.properties
在conf目錄下創建該文件,並且寫入以下內容:
[code]#所有節點列表,其中controller是一個邏輯結點,負責負載均衡控制,
#如果JkMount中的URL指定給了controller就表示這個請求會被自動散列到某個物理節點上。
#注意:真正負責處理請求的tomcat的名稱(這裡就是tomcat1,tomcat2)必須於它們在conf/server.xml
#文件中配置的jvmRout的屬性值是一致的!
worker.list = controller,tomcat1,tomcat2
#========tomcat1========
worker.tomcat1.port=9009 #ajp13 端口號,在tomcat1下server.xml配置,默認8009,必須要與修改後的一致
worker.tomcat1.host=localhost #tomcat1的主機地址,如不為本機,請填寫ip地址
worker.tomcat1.type=ajp13
worker.tomcat1.lbfactor=1 #tomcat1 server的加權比重,值越高,分得的請求越多
#========tomcat2========
worker.tomcat2.port=10009 #ajp13 端口號,在tomcat2下server.xml配置,默認8009,必須要與修改後的一致
worker.tomcat2.host=localhost #tomcat2的主機地址,如不為本機,請填寫ip地址
worker.tomcat2.type=ajp13
worker.tomcat2.lbfactor=1 #tomcat2 server的加權比重,值越高,分得的請求越多
#========controller,負載均衡控制器========
worker.controller.type=lb
worker.controller.balance_workers=tomcat1,tomcat2 #指定分擔請求的tomcat,舊版本中的balanced_workers,已不再推薦使用!
worker.controller.sticky_session=1 #sticky_session為1表示,
#當某一 client的session創建之後,後續由該客戶端發起的請求,也就是這個session的所有請求都始終由第一次處理該請求的結點
#負責處理(除非該結點掛掉)
到此,所有配置均已完成。
5. 測試
創建web應用程序
使用Eclipse創建Maven webapp程序ClusterTest,在WEB-INF的web.xml中加上
<distributable/>
元素,要放在servlet元素前面(不然不報錯),然後打成war包分別拷貝到tomcat1與tomcat2的webapps下。
也就是在需要集群的應用的web.xml中加上屬性distributable,表明該應用可多應用分流處理,能進行Session的復制
多刷新幾次浏覽器窗口,你將會看到在兩個Tomcat窗口都打印出相同的SessionID及其中的值,並且每次刷新後打印的結果都一樣的。
如果不為應用的web.xml加上 ,同樣測試index.jsp頁面,每次刷新分流到不同的tomcat上都會產生不一樣的SessionID,在同一個tomcat上也是間隔出現不同的sessionID。
即如果不加上
<distributable/>
元素,那麼同一個客戶的多次訪問sessionID是不會復制的,也就是相同客戶會一直改變。
啟動兩個tomcat和apache httpd
(1) 啟動apache httpd
cd ~/apache/apache/bin
sudo ./apachectl start
(2) 啟動tomcat1
cd ~/apache/tomcat1/bin
chmod 755 *.sh
startup.sh
(3) 啟動tomcat2
cd ~/apache/tomcat2/bin
chmod 755 *.sh
startup.sh
多次訪問localhost/ClusterTest(遠程使用ip/ClusterTest, httpd 默認是80端口)
驗證
分別進入~/apache/tomcat1/logs與~/apache/tomcat2/logs,查看localhost_access_log.yyyy-MM-dd.txt,可以看出一些訪問請求信息,說明請求被隨機分配給了兩個tomcat交替執行。
另外一種驗證方法
在應用程序中的index.jsp中寫入內容為:
[code]<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="java.text.SimpleDateFormat"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Tomcat集群測試</title>
</head>
<body>
服務器信息:
<%
String dtm = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
System.out.println("["+request.getLocalAddr()+":"+ request.getLocalPort()+"]" + dtm);
out.println("<br>["+request.getLocalAddr()+":" +request.getLocalPort()+"]" + dtm+"<br>");
%>
session分發:
<%
session.setAttribute("name","dennisit");
System.out.println("[session分發] session id:"+session.getId());
out.println("<br>[session分發] session id: " + session.getId()+"<br>");
%>
絕對路徑
<%
String absPath = new java.io.File(application.getRealPath(request.getRequestURI())).getParent();
System.out.println("Abs Path:"+absPath);
out.println("Abs Path:"+absPath);
%>
</body>
</html>
那麼多次在浏覽器中輸入http://機器的ip/ClusterTest/ 會打印出不同的絕對路徑(tomcat1與tomcat2):
應用程序的web.xml沒有配置distributable元素,那麼:
在不同的tomcat上,sessionID會不一樣,因為不會進行sessionID拷貝。上面的截圖是tomcat沒有Engine元素配置jvmRoute屬性下的。
下面四種情況是應用程序的web.xml配置了distributable元素
tomcat的Engine元素配置了jvmRoute, worker.controller.sticky_session=1:
說明相同的客戶一直在同一個tomcat上,並且sessionID一致。
tomcat的Engine元素配置了jvmRoute並且worker.controller.sticky_session=0:
說明相同的客戶會交替在不同的tomcat上,並且sessionID一致。
tomcat的Engine元素沒有配置jvmRoute, worker.controller.sticky_session=1:
說明相同的客戶會交替在不同的tomcat上,並且sessionID一致,只是後面不會sessionID不會出現.tomcat1或者.tomcat2。
tomcat的Engine元素沒有配置jvmRoute並且worker.controller.sticky_session=0:
說明相同的客戶會交替在不同的tomcat上,並且sessionID一致,只是後面不會sessionID不會出現.tomcat1或者.tomcat2。
上述便說明了:
(1) 如果你需要設置相同的客戶端一直在同一個tomcat服務器上(除非這台掛掉樂),那麼需要設置tomcat的jvmRoute與worker.controller.sticky_session=1。
(2) 如果你需要設置相同的客戶端可以隨機分配到不同的tomcat服務器上,那麼需要設置tomcat的jvmRoute與worker.controller.sticky_session=0,或者不設置tomcat的jvmRoute。
因此根據不同的需要配置不同。
如果在不同的機器上訪問,上述的四種配置都會進行分流,因為是不同的客戶。
完全集群(水平集群)
tomcat分別部署在多台機器上,步驟與單機情況差不多。其中不同機器上的tomcat的server.xml各端口可以一樣,不過在
<Engine>
元素的jvmRoute屬性必須配置不一樣的名字,如tomcat1、tomcat2 …, 並且在workers.properties文件中配置正確:
worker.controller.balance_workers=tomcat1,tomcat2…
以及在每個tomcat的屬性配置正確,如:
worker.tomcat1.port=自己的ajp13端口號
worker.tomcat1.host=自己的ip
問題
在啟動httpd時 ./apachectl start 報(13)Permission denied: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open logs
解決: 需要管理員權限 使用sudo 運行該命令
參考資料
[1] /content/543613.html
[2] tomcat官方關於tomcat集群配置的文檔:
http://tomcat.apache.org/tomcat-6.0-doc/cluster-howto.html
[3] tomcat官方關於Apache Tomcat Connector的文檔
http://tomcat.apache.org/connectors-doc/webserver_howto/apache.html
[4] apache官方關於apache的安裝文檔
http://httpd.apache.org/docs/2.2/en/install.html
[5] apache官方關於configure參數的文檔
http://httpd.apache.org/docs/2.2/en/programs/configure.html#installationdirectories