首先,我需要开发一个web应用,来测试我的集群,于是,我创建了一个动态web项目,取名叫sessiontest. 在此项目中,只有两个jsp文件。
第一个select.jsp. 这个文件相当于一个购物车,将用户选中的内容提交,并展示用户选中的物品。
01
<%@ page language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
02
<!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
03
<html>
04
<head>
05
<title>Your selections</title>
06
</head>
07
<body>
08
<%
09
String selections = (String)session.getAttribute(
"selections"
);
10
selections=selections==
null
?
""
:selections;
11
%>
12
<form action=
"successful.jsp"
method=
"post"
>
13
<p>What
do
you prefer?</p>
14
<p><input type=
"checkbox"
name=
"car"
value=
"car"
<%
15
if
(selections.indexOf(
"car"
)>-
1
) out.print(
"checked=\"checked\""
); %> />Car</p>
16
<p><input type=
"checkbox"
name=
"bike"
value=
"bike"
<%
17
if
(selections.indexOf(
"bike"
)>-
1
) out.print(
"checked=\"checked\""
); %> />Bike</p>
18
<p><input type=
"checkbox"
name=
"train"
value=
"train"
<%
19
if
(selections.indexOf(
"train"
)>-
1
) out.print(
"checked=\"checked\""
); %> />Train</p>
20
<p><input type=
"checkbox"
name=
"plane"
value=
"plane"
<%
21
if
(selections.indexOf(
"plane"
)>-
1
) out.print(
"checked=\"checked\""
); %> />Plane</p>
22
<input type=
"submit"
value=
"Submit"
/>
23
</form>
24
</body>
25
</html>
第二个文件是successful.jsp, 它将select.jsp提交的选项包存入session中。
01
<%@ page language=
"java"
contentType=
"text/html; charset=UTF-8"
02
pageEncoding=
"UTF-8"
%>
03
<!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
04
<html>
05
<head>
06
<title>Successful</title>
07
</head>
08
<body>
09
<%
10
StringBuffer sb =
new
StringBuffer();
11
if
(
null
!=request.getParameter(
"car"
))sb.append(
"car;"
);
12
if
(
null
!=request.getParameter(
"bike"
))sb.append(
"bike;"
);
13
if
(
null
!=request.getParameter(
"train"
))sb.append(
"train;"
);
14
if
(
null
!=request.getParameter(
"plane"
))sb.append(
"plane;"
);
15
16
session.setAttribute(
"selections"
, sb.toString());
17
%>
18
Successful, please go back to check.<br>
19
<a href=
"select.jsp"
>Back</a>
20
</body>
21
</html>
将项目导出为sessiontest.war,并部署到tomcat中的用户界面为:
http:
http:
接下来,我会使用这个web应用来测试我的集群。
最简单的集群
如下图所示,最简单的集群并不是一个真正的集群,tomcat1和tomcat2只是运行了同一个应用,比如我的sessiontest.war, 它们两个之间并没有任何通信,共享。Apache只是根据访问量进行负载平衡。一旦其中一个tomcat关机,所有的request都将被转发到另一个tomcat上去。而之前工作在第一个tomcat上的用户会丢失信息,比如session,运行环境。
首先在本机上安装两个tomcat, 也就是生成
2
分tomcat的copy。下面将这两个tomcat分别以tomcat1和tomcat2命名。
维持tomcat1的port的配置,但需要向其server.xml中添加一个属性 jvmRoute=
"node1"
:
1
<Engine name=
"Catalina"
defaultHost=
"localhost"
jvmRoute=
"node1"
>
修改tomcat2的port的配置,并向其server.xml中添加一个属性 jvmRoute=
"node2"
:
1
<Server port=
"8006"
shutdown=
"SHUTDOWN"
>
2
<Connector port=
"8081"
protocol=
"HTTP/1.1"
connectionTimeout=
"20000"
redirectPort=
"8443"
/>
3
<Connector port=
"8010"
protocol=
"AJP/1.3"
redirectPort=
"8443"
/>
4
<Engine name=
"Catalina"
defaultHost=
"localhost"
jvmRoute=
"node2"
>
将之前的应用sessiontest.war分别部署到两个tomcat上。测试localhost:
8080
/sessiontest/select.jsp和localhost:
8081
/sessiontest/select.jsp。这样,集群的两个node就跑起来了。
配置apache httpd,开启负载平衡。基于上一篇文章配好的 的网站,我将把集群挂在这个网站下面,将下面的配置加入virtualhost中。不要忘记开启proxy_balancer module。
01
<VirtualHost *:
443
>
02
ServerName
03
ProxyPass /sessiontest balancer:
04
<Proxy balancer:
05
BalancerMember ajp:
06
BalancerMember ajp:
07
ProxySet stickysession=JSESSIONID
08
ProxySet lbmethod=byrequests
09
</Proxy>
10
</VirtualHost>
stickysession的意义在于,一个用户可以根据其cookie中的jsessionid固定的访问一个tomcat,这样不会在两个tomcat之间跳来跳去而丢失运行信息和session。
开启集群节点之间的通信
接下来,我将开启tomcat1和tomcat2之间的通信,这样它们可以共享session和一些内存信息。一个正在tomcat1上访问的用户,因为tomcat1的关机而转移到tomcat2上,也不会丢失session信息了。
修改原web应用,在sessiontest的web.xml中添加一段。表明此应用需要在cluster之间进行同步。然后重新部署此应用。
1
<distributable/>
分别修改tomcat1和tomcat2的server.xml,加入相同的一段代码。 Tomcat使用组播技术,使得各个node之间建立连接,唯一需要声明的是你的Recervier端口,在
4000
-
4999
之间任选一个。
01
<Cluster className=
"org.apache.catalina.ha.tcp.SimpleTcpCluster"
02
channelSendOptions=
"8"
>
03
04
<Manager className=
"org.apache.catalina.ha.session.DeltaManager"
05
expireSessionsOnShutdown=
"false"
06
notifyListenersOnReplication=
"true"
/>
07
08
<Channel className=
"org.apache.catalina.tribes.group.GroupChannel"
>
09
<Membership className=
"org.apache.catalina.tribes.membership.McastService"
10
address=
"228.0.0.4"
11
port=
"45564"
12
frequency=
"500"
13
dropTime=
"3000"
/>
14
<Receiver className=
"org.apache.catalina.tribes.transport.nio.NioReceiver"
15
address=
"auto"
16
port=
"4444"
17
autoBind=
"100"
18
selectorTimeout=
"5000"
19
maxThreads=
"6"
/>
20
21
<Sender className=
"org.apache.catalina.tribes.transport.ReplicationTransmitter"
>
22
<Transport className=
"org.apache.catalina.tribes.transport.nio.PooledParallelSender"
/>
23
</Sender>
24
<Interceptor className=
"org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"
/>
25
<Interceptor className=
"org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"
/>
26
</Channel>
27
28
<Valve className=
"org.apache.catalina.ha.tcp.ReplicationValve"
29
filter=
""
/>
30
<Valve className=
"org.apache.catalina.ha.session.JvmRouteBinderValve"
/>
31
32
<Deployer className=
"org.apache.catalina.ha.deploy.FarmWarDeployer"
33
tempDir=
"/tmp/war-temp/"
34
deployDir=
"/tmp/war-deploy/"
35
watchDir=
"/tmp/war-listen/"
36
watchEnabled=
"false"
/>
37
38
<ClusterListener className=
"org.apache.catalina.ha.session.ClusterSessionListener"
/>
39
</Cluster>
分别启动tomcat1和tomcat2.请等一个启动完再启动另一个。集群创建完毕。
为了测验集群,我将负载平衡的stickysession关掉。请注释掉配置中的ProxySet stickysession=JSESSIONID. 重启apache. 这时候用户的request会在tomcat1和tomcat2之间跳来跳去。
访问 你会发现即使request在两个tomcat之间跳来跳去,但购物车里的信息永远不会丢失。