天天看點

tomcat叢集概述配置Tomcat叢集測試Tomcat叢集Apache負載均衡Tomcat叢集和Session複制詳解Apache負載均衡詳解



概述

Tomcat是J2EE開發當中使用最多的應用伺服器,本章就來介紹一下使用Apache2應用伺服器加上Tomcat6一起實作應用叢集與負載均衡。這裡我們使用的Tomcat為7.0.26;Apache版本為2.2,如果您機器上沒有,請到www.apache.org上下載下傳,Tomcat7.0.26有兩個,我們這裡稱之為tomcat1和tomcat2,下文中如果沒特指tomcat1還是tomcat2,那麼tomcat1和tomcat2都要進行操作,apache2.2隻有一個。是以都準備好之後,接下來我們就來看看如何進行配置。

配置Tomcat叢集

打開tomcat下conf/server.xml檔案,找到<Engine name="Catalina" defaultHost="localhost">,增加jvmRoute屬性,以支援AJP負載均衡。

tomcat1改為<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">

tomcat2改為<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm2">

因為我們的兩個Tomcat在是一台機器上運作,是以我們有必要修改其中一個Tomcat采用端口,以保證兩個同時運作時不會産生沖突(當然,如果兩個tomcat不在一台機器上運作,那麼這個步驟就可以省略)。我們這裡修改的是tomcat2,打開tomcat2下conf/server.xml檔案,修改如下:

<Server port=

"8005"

shutdown=

"SHUTDOWN"

>

修改為

<Server port=

"9005"

shutdown=

"SHUTDOWN"

>

<Connector port=

"8080"

protocol=

"HTTP/1.1"

connectionTimeout=

"20000"

redirectPort=

"8443"

/>

修改為

<Connector port=

"9080"

protocol=

"HTTP/1.1"

connectionTimeout=

"20000"

redirectPort=

"9443"

/>。

<Connector port=

"8009"

protocol=

"AJP/1.3"

redirectPort=

"8443"

/>

修改為

<Connector port=

"9009"

protocol=

"AJP/1.3"

redirectPort=

"9443"

/>

端口修改完成,我們需要來配置一個叢集環境中的session複制,打開tomcat下conf/server.xml檔案,在<Engine></Engine>節點内加入下面代碼:

<Cluster className=

"org.apache.catalina.ha.tcp.SimpleTcpCluster"

channelSendOptions=

"6"

>

<Manager className=

"org.apache.catalina.ha.session.BackupManager"

expireSessionsOnShutdown=

"false"

notifyListenersOnReplication=

"true"

mapSendOptions=

"6"

/>

<!--

<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=

"5000"

selectorTimeout=

"100"

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"

/>

<Interceptor className=

"org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"

/>

</Channel>

<Valve className=

"org.apache.catalina.ha.tcp.ReplicationValve"

filter=

".*\.gif|.*\.js|.*\.jpeg|.*\.jpg|.*\.png|.*\.htm|.*\.html|.*\.css|.*\.txt"

/>

<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.ClusterSessionListener"

/>

</Cluster>

其中同台機器上的TCP端口不能有沖突,是以在上面的配置當中,對于Tomcat2需要修改節點Culster->Channel->Reciver的port屬性,預設為5000,tomcat識别的範圍為4000-4100,這裡把tomcat2的這個port屬性修改為4001。

這些配置都完成後,接下來我們就可以在這兩個Tomcat當中添加一個簡單應用來實際測試一下叢集效果。

測試Tomcat叢集

在tomcat/webapps下,建立一檔案夾為test,test檔案夾下建立WEB-INF目錄,建立标準web.xml檔案,檔案中一定要加入<distributable/>标簽以支援分布式應用。

web.xml檔案内容如下:

<?xml version=

"1.0"

encoding=

"ISO-8859-1"

?> <web-app xmlns=

"<a href="http://java.sun.com/xml/ns/javaee" target="_blank" rel="external nofollow" "="">http://java.sun.com/xml/ns/javaee"

xmlns:xsi=

"<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank" rel="external nofollow" "="">http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation=

"http://java.sun.com/xml/ns/javaee                       <a href="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" target="_blank" rel="external nofollow" "="">http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"

version=

"3.0"

metadata-complete=

"true"

>

<display-name>Welcome to Test Tomcat7 Cluster</display-name>  

<description>Welcome to Test Tomcat7 Cluster</description>

<distributable/>

</web-app>

在test檔案夾下建立test.jsp檔案,檔案内容如下:

<%@ page contentType=

"text/html; charset=UTF-8"

%>

<%@ page

import

=

"java.util.*"

%>

<html><head><title>Cluster App Test</title></head>

<body>

Server Info:

<%

out.println(request.getLocalAddr() +

" : "

+ request.getLocalPort()+

"<br>"

);%>

<%

out.println(

"<br> ID "

+ session.getId()+

"<br>"

);

// 如果有新的 Session 屬性設定

String dataName = request.getParameter(

"dataName"

);

if

(dataName !=

null

&& dataName.length() >

) {

String dataValue = request.getParameter(

"dataValue"

);

session.setAttribute(dataName, dataValue);

}

out.println(

"<b>Session 清單</b><br>"

);

System.out.println(

"============================"

);

Enumeration e = session.getAttributeNames();

while

(e.hasMoreElements()) {

String name = (String)e.nextElement();

String value = session.getAttribute(name).toString();

out.println( name +

" = "

+ value+

"<br>"

);

System.out.println( name +

" = "

+ value);

}

%>

<form action=

"index.jsp"

method=

"POST"

>

名稱:<input type=text size=

20

name=

"dataName"

>

<br>

值:<input type=text size=

20

name=

"dataValue"

>

<br>

<input type=submit>

</form>

</body>

</html>

這個簡單的JSP可以把送出的值儲存到Session當中,同時顯示出來。

啟動tomcat1,等tomcat1啟動完成後,啟動tomcat2。先通路tomcat1對應的test工程http://localhost:8080/test.jsp頁面,輸入值并送出,然後把連結轉到tomcat2對應的test工程http://localhost:9080/test.jsp頁面,看看session内容是否相同,如果相同則說明session複制成功,也就是群內建功。

接下來我們在這兩個Tomcat前端添加一個Apache Http Server,所有通路請求通過這個Http Server進行轉發,根據tomcat1或2的忙碌情況決定轉發給tomcat1或者是tomcat2去處理使用者請求。

Apache負載均衡

打開并修改apache安裝目錄下conf/httpd.conf檔案,找到下面這些内容:

#LoadModule negotiation_module modules/mod_negotiation.so

#LoadModule proxy_module modules/mod_proxy.so

#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so

#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so

#LoadModule proxy_connect_module modules/mod_proxy_connect.so

#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so

#LoadModule proxy_http_module modules/mod_proxy_http.so

去掉這些#,也就是啟用這些被注釋掉的功能,并且在檔案的未尾處添加下面這些内容:

ProxyPass / balancer:

//cluster/ stickysession=JSESSIONID

ProxyPassReverse / balancer:

//cluster/

<proxy balancer:

//cluster>

BalancerMember ajp:

//127.0.0.1:8009 loadfactor=1 route=jvm1

BalancerMember ajp:

//127.0.0.1:9009 loadfactor=1 route=jvm2

</proxy>

重新啟動apache,以apache作為負載均衡器的tomcat叢集建立完成,所有請求都可以通過請求apache http server,通過這個http server轉發到具體的tomcat伺服器。

Tomcat叢集和Session複制詳解

Cluster

<Cluster className=

"org.apache.catalina.ha.tcp.SimpleTcpCluster"

channelSendOptions=

"8"

>

Tomcat叢集的主元素,在Cluster元素裡面配置了叢集的詳細資訊。目前Tomcat僅提供了org.apache.catalina.ha.tcp.SimpleTcpCluste作為唯一的實作類。下面是Cluster詳細屬性:

屬性 描述
className Cluste的實作類,目前org.apache.catalina.ha.tcp.SimpleTcpCluste作為唯一的實作類。
channelSendOptions Session發送方式,預設值為:8。當消息通過SimpleTcpCluster發送時,用來決定如何發送資訊。

int

options= Channel.SEND_OPTIONS_ASYNCHRONOUS |

Channel.SEND_OPTIONS_SYNCHRONIZED_ACK | Channel.SEND_OPTIONS_USE_ACK;

Channel.SEND_OPTIONS_SYNCHRONIZED_ACK =

0x0004

Channel.SEND_OPTIONS_ASYNCHRONOUS =

0x0008

Channel.SEND_OPTIONS_USE_ACK =

0x0002

如果使用異步(ASYNCHRONOUS)加應答(USE_ACK)方式來發送消息,那麼值應該是10(8+2)或者0x000B。

Manager

<Manager className=

"org.apache.catalina.ha.session.DeltaManager"

expireSessionsOnShutdown=

"false"

notifyListenersOnReplication=

"true"

/>

Manager用來在Tomcat節點之前複制Session,目前有兩個實作類,分别為:org.apache.catalina.ha.session.DeltaManagert和org.apache.catalina.ha.session.BackupManager。DeltaManager調用SimpleTcpCluster.send方法來發送資訊,複制并發送Session到叢集下所有的節點,不管這個節點有沒有部署目前程式。

BackupManager通過自己直接調用channel來發送資訊,複制并發送Session到叢集下部署了目前程式的節點。

DaltaManager的優點是經過實踐确認和證明的,非常可靠,而BackupManager在可靠性上不如DaltaManager。DaltaManager缺點是要求叢集節點必須部署了同樣的程式,節點必須是同種類的。

下面是Manager的屬性描述:

屬性 描述
className Manager的實作類。
notifyListenersOnReplication 如果設定為true,當session屬性被複制和移動的時候,session listener被通知
expireSessionsOnShutdown 當一個web程式被結束時,tomcat分發銷毀指令到每個Session,并通知所有session listener執行。當叢集下某個節點被停止時,如果想銷毀所有節點下的的Session,設定為true,預設為false 。

BackupManager屬性:

屬性 描述
mapSendOptions BackupManager通過一個可複制的map來實作session接受和發送,通過設定mapSendOptions來設定這個map以何種方式來發送資訊,預設為:6(asynchronous)。

Channel

<Channel className=

"org.apache.catalina.tribes.group.GroupChannel"

>

Channel是Apache Tribes的主元件,channel管理一組子元件,并和它們一起組成了tomcat執行個體間的通訊架構。在tomcat叢集中,DeltaManager通過SimpleTcpCluster調用channel來實作資訊傳遞,而BackupManager自己調用channel以及子元件這些元件來實作資訊傳遞。ReplicatedContext也會調用channel傳遞context屬性。

下面是channel子元件

*MemberShip

MemberShip元件自動檢索發現叢集裡的新節點或已經停止工作的節點,并發出相應的通知。預設使用多點傳播(Multicast)實作。

*Sender

Sender元件管理從一個節點發送到另外一個節點的出站連接配接和資料資訊,允許資訊并行發送。預設使用TCP Client Sockets。

*Receiver

Receiver元件負責監聽接收其他節點傳送過來的資料。預設使用non-blocking TCP Server sockets。

*Interceptor

Channel通過Interceptor堆棧進行消息傳遞,在這裡可以自定義消息的發送和接收方式,甚至MemberShip的處理方式。

Value

<Valve className=

"org.apache.catalina.ha.tcp.ReplicationValve"

filter=

" .*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.css;.*\.txt; "

/>

<Valve className=

"org.apache.catalina.ha.session.JvmRouteBinderValve"

/>

Cluster下的Value同其他tomcat value一樣,value在調用Http Request 鍊中起着攔截器的作用,用來決定什麼情況下資料需要被複制。

org.apache.catalina.ha.tcp.ReplicationValve,ReplicationValue在Http Request結尾判斷目前資料是否需要被複制。它的具體屬性描述如下表所示:

屬性 描述
className org.apache.catalina.ha.tcp.ReplicationValve,ReplicationValue
filter Filter内容為url或者檔案結尾,當通路連結配置filter時,不論實際session有沒有改變,叢集會認為session沒有任何變化,進而不會複制和發送改變的session屬性。Filter寫法如下:filter=" .\.gif;.\.js;.\.jpg;.\.png;.\.css;.\.txt; "。filter使用正規表達式,每個url或者字尾以逗号分開。

Deployer

使叢集支援farmed deployment。這個功能還很弱,在變化和更改中。

ClusterListener

<ClusterListener className=

"org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"

/>

<ClusterListener className=

"org.apache.catalina.ha.session.ClusterSessionListener"

/></Cluster>

Clusterlistener用來追蹤資訊發送和接收。JvmRouteSessionIDBinderListener用來監聽session id的變化。這個listener僅當使用mod_jk并且屬性有jvmRoute時才起作用。當session id 改變後,JvmRouteBinderValve将廣播這個資訊并被此listener捕獲。ClusterSessionListener用來監聽叢集元件接收資訊,當使用DeltaManager的時候,資訊被叢集接收,并通過ClusterSessionListener傳遞給Session Manager。

Apache負載均衡詳解

ProxyPass / balancer:

//cluster/ stickysession=JSESSIONID

ProxyPassReverse / balancer:

//cluster/

<proxy balancer:

//cluster>

BalancerMember ajp:

//127.0.0.1:8009 loadfactor=1 route=jvm1

BalancerMember ajp:

//127.0.0.1:9009 loadfactor=1 route=jvm2

</proxy>

通過Apache自帶的mod_proxy某塊來使用代理技術連接配接tomcat, ajp_proxy需要tomcat提供ajp服務。