征服 Apache + Tomcat

.征服Apache+Tomcat博客分类:ServerArchitecture/Basic

ApacheTomcat应用服务器配置管理Ubuntu.

Apache和Tomcat原本就是一家,更是一家亲!Apache与Tomcat整合,无非是将Apache作为前端根据请求路径、端口、代理分发给多个Tomcat,以到达转发和负载均衡的目的!同时,通过Apache和Tomcat相互作用,进行粘性会话,会话拷贝构建集群!这一切的最终结果就是“云服务”!不要说Session不重要,当下火爆的团购,如果离开Session还能快活多久?如何保证Session同步,仍然是不能回避的问题!

这里要说的是基于HTTP和AJP跳转方式的负载均衡实现,关于JK,由于效率问题一直成为诟病,并且mod_jk2模块已经不再被更新了,这里就不折腾它的复杂配置了!

至于说Apache+Tomcat+SSL,并不是难题!只要完成了Apache+SSL然后配置相应的负载均衡、反向代理等等就可以达到目的,相关Apache+SSL参考征服Apache+SSL

UbuntuServer10.04版本,Apache选用2.2.14,Tomcat选用6.0.24。

相关内容:

征服Apache+SSL

征服Apache+SVN

征服Apache+SVN+LDAP

征服Apache+Tomcat

征服Nginx

征服Nginx+Tomcat

步骤:

1.安装Apache基本模块

2.后台监控

3.负载均衡简单测试

4.配置Tomcat相关模块(AJP)

5.保持Session唯一,粘性会话

6.Tomcat集群,Session复制

1.安装Apache相关模块

负载均衡需要的主要是代理模块!

经过几次Apache配置尝试,在Ubuntu下配置Apache实在是太容易了。加载什么模块、取消什么模块两个命令搞定。

Shell代码

1.#启用模块

2.sudoa2enmod<model>

3.#禁用模块

4.sudoa2dismod<model>

#启用模块

sudoa2enmod<model>

#禁用模块

sudoa2dismod<model>

这里,我们需要让Apache提供代理服务,其中又包含基于http、ftp、ajp等等协议的代理功能,同时还需要负载均衡模块。我们可以通过命令逐个加载:

Shell代码

1.#代理核心模块

2.sudoa2enmodproxy

3.#代理AJP模块

4.sudoa2enmodproxy_ajp

5.#代理负载均衡模块

6.sudoa2enmodproxy_balancer

7.#代理HTTP模块

8.sudoa2enmodproxy_http

9.#代理FTP模块

10.sudoa2enmodproxy_ftp

#代理核心模块

sudoa2enmodproxy

#代理AJP模块

sudoa2enmodproxy_ajp

#代理负载均衡模块

sudoa2enmodproxy_balancer

#代理HTTP模块

sudoa2enmodproxy_http

#代理FTP模块

sudoa2enmodproxy_ftp

完成上述操作后,系统会提示重启Apache!

先不着急重启,现学现卖,了解下Apache的目录结构。在Ubuntu下配置Apache主要是在/etc/apache2目录下:

分述:

•apache2.conf核心配置文件,一般不需要修改!

•conf.d目录,里面包含了一些字符集设置,文档等设置!

•dav_svn.authz和dav_svn.passwd是前面做SVN时,相关权限、密码文件。

•envvars定义了运行时的用户身份——www-data。

•httpd.conf是Apache留给我们自己折腾的配置文件,默认为空。apache2.conf会加载这个文件。

•ports.conf端口默认配置。apache2.conf会加载这个文件。

•magic为mod_mime_magic模块服务。

•mods-enabled和mods-availablemods-enabled会被apache2.conf加载,里面包含*.load和*.conf文件。*.load文件中是加载相应的模块(位于/usr/lib/apache2/modules/中),而*.conf中是对应的基本配置。但这些文件其实都是链接到mods-available中相应的文件上。当我们通过a2enmod操作时,实际上正是操作了这些软链接。

•sites-available和sites-enabled与mods-enabled和mods-available的关系类似,只是其中包含的是站点内容。

罗嗦了一堆,下面配置负载均衡部分。

执行修改:

Shell代码

1.sudovi/etc/apache2/mods-available/proxy.conf

sudovi/etc/apache2/mods-available/proxy.conf

上图红框中的内容是原始内容,白框中的内容是我新加的部分。

注意红框中的配置:

Conf代码

1.<Proxy*>

2.AddDefaultCharsetoff

3.Orderdeny,allow

4.#Denyfromall

5.Allowfromlocalhostip6-localhost

6.</Proxy>

<Proxy*>

AddDefaultCharsetoff

Orderdeny,allow

#Denyfromall

Allowfromlocalhostip6-localhost

</Proxy>

在默认配置中,Denyfromall处于可用状态。当我们配置其他代理节点时,将导致杜绝访问!使用Allowfromlocalhostip6-localhost限制仅允许本机访问!

再说,白框中的内容:

Conf代码

1.<Proxybalancer://zlex>

2.BalancerMemberhttp://localhost:8080/

3.BalancerMemberhttp://192.168.49.1:8080/

4.</Proxy>

<Proxybalancer://zlex>

BalancerMemberhttp://localhost:8080/

BalancerMemberhttp://192.168.49.1:8080/

</Proxy>

这里,我配置了一个负载均衡节点balancer://zlex,其中包含了两个服务http://localhost:8080/和http://192.168.49.1:8080/,一个是虚拟机上的Tomcat、一个是真机上的Tomcat。这里使用的Http的转发方式,当然,使用AJP未尝不可,稍后详述!

这里的节点次序会有一个先后关系,Apache会将请求按照FIFO的方式调度顺次分配到各个节点上!如果其中有一个节点挂掉,将跳过该节点顺次寻找可用节点。

再说代理和反向代理:

Conf代码

1.ProxyPass/zlexbalancer://zlex

2.ProxyPassReverse/zlexbalancer://zlex

ProxyPass/zlexbalancer://zlex

ProxyPassReverse/zlexbalancer://zlex

这里配置,如果要访问/zlex路径时,将跳转到balancer://zlex上,也就是享受负载均衡!

2.后台监控

我们如何知道某个节点负载多少,响应时间多久,服务是否正常呢?Apache提供了负载均衡监控平台:http://localhost/balancer-manager。但是,这个服务默认是不存在。

除了用于负载均衡配置、监控的balancer-manager还有http://localhost/server-status和http://localhost/server-info

我们需要添加info模块:

Shell代码

1.#系统信息模块

2.sudoa2enmodinfo

#系统信息模块

sudoa2enmodinfo

同时,为了能够使用balancer-manager,我们需要配置:

Conf代码

1.<Location/balancer-manager>

2.SetHandlerbalancer-manager

3.OrderDeny,Allow

4.#Denyfromall

5.Allowfromlocalhostip6-localhost

6.</Location>

<Location/balancer-manager>

SetHandlerbalancer-manager

OrderDeny,Allow

#Denyfromall

Allowfromlocalhostip6-localhost

</Location>

把这段代码放到哪?由于它同属系统信息配置,我把它放到了info.conf中,说白了就是照猫画虎:

Shell代码

1.sudovi/etc/apache2/mods-available/info.conf

sudovi/etc/apache2/mods-available/info.conf

注意,这段代码放到了<IfModulemod_info.c>和</IfModule>之间!

现在,我们重启Apache:

Shell代码

1.sudo/etc/init.d/apache2restart

sudo/etc/init.d/apache2restart

来看看管理界面http://localhost/balancer-manager:

我们再来看看服务器基本信息http://localhost/server-info:

上述两个服务需要加载info模块,而服务器状态(server-status)不需要http://localhost/server-status:

3.负载均衡简单测试

疯狂访问http://localhost/zlex,直到手酸眼烦!

我这里故意使用不同了Tomcat界面,来验证自己的配置是否生效。更疯狂的是,我甚至把节点指向了百度、搜狐,来测试负载均衡的效果。如果你细致观察,Apache是将请求顺次分配到各个节点上的。

如果其中一个节点发生问题(例如,强行关闭一个Tomcat,或配置一个错误节点)Apache将会经过几次尝试后,绕过这个问题节点,寻找可以成功访问的节点。如果这个节点恢复正常使用,Apache将在该Tomcat恢复正常工作后大约1分钟内将该节点标识为可用!

现在,再看看现在的后台(http://localhost/balancer-manager)啥样子:

如果我们控制一个节点的状态是否可用,该怎么做:

涉及到负载量,session同步等等,我们最后讨论!

4.配置Tomcat相关模块(AJP)

基于Http协议分发并不复杂,但AJP效果更好!一次诡异事件中,内网访问正常,外网访问多次失败,最后通过AJP方式完美解决了!

在Tomcat中配置AJP也很简单,修改server.xml开启AJP模块:

Shell代码

1.sudovi/etc/tomcat6/server.xml

sudovi/etc/tomcat6/server.xml

开启AJP配置:

Xml代码

1.<Connectorport="8009"protocol="AJP/1.3"

2.URIEncoding="UTF-8"

3.redirectPort="8443"/>

<Connectorport="8009"protocol="AJP/1.3"

URIEncoding="UTF-8"

redirectPort="8443"/>

注意使用的端口port="8009",字符集URIEncoding="UTF-8",这是输入框、请求字符集乱码的入口!

接下里就可以通过AJP方式进行节点分发了。修改/etc/apache2/mods-available/proxy.conf:

Shell代码

1.sudovi/etc/apache2/mods-available/proxy.conf

sudovi/etc/apache2/mods-available/proxy.conf

将http改为ajp,将8080改为8009:

Conf代码

1.<Proxybalancer://zlex>

2.BalancerMemberajp://localhost:8009/

3.BalancerMemberajp://192.168.49.1:8009/

4.</Proxy>

<Proxybalancer://zlex>

BalancerMemberajp://localhost:8009/

BalancerMemberajp://192.168.49.1:8009/

</Proxy>

重启Apache:

Shell代码

1.sudo/etc/init.d/apache2restart

sudo/etc/init.d/apache2restart

再看看管理界面http://localhost/balancer-manager

至此,我们完成了基本负载均衡的基本配置!

/etc/apache2/mods-available/proxy.conf还有一些属性:

noFailOver是否打开失败转移,On|Off,默认为Off,添加在ProxyPass后面,如:

Conf代码

1.ProxyPass/zlexbalancer://zlexstickySession=JSESSIONIDnoFailOver=On

ProxyPass/zlexbalancer://zlexstickySession=JSESSIONIDnoFailOver=On

如果这样配置,当提供给你服务的服务器发生异常,那么你将一直看着它返回给你503,直到系统恢复正常!

loadfactor表示后台服务器负载到由Apache发送请求的权值,默认值为1添加在BalancerMember后面:

Conf代码

1.<Proxybalancer://zlex>

2.BalancerMemberajp://localhost:8009/

3.BalancerMemberajp://192.168.49.1:8009/

4.</Proxy>

<Proxybalancer://zlex>

BalancerMemberajp://localhost:8009/

BalancerMemberajp://192.168.49.1:8009/

</Proxy>

可以实现三种策略:

1.轮询均衡策略的配置

2.按权重分配均衡策略的配置

3.权重请求响应负载均衡策略的配置

5.Session唯一,粘性会话

Apache已经可以轻松将内容处理的工作分配给各个Tomcat了!

当然,这还不够,Session还是个问题!

WHY?

我们来做一系列修改,来检测Session到底出现了什么问题!

先来改造Tomcat,修改server.xml:

Shell代码

1.sudovi/etc/tomcat6/server.xml

sudovi/etc/tomcat6/server.xml

修改<Engine/>节点,增加jvmRoute属性:

Xml代码

1.<Engine

2.name="Catalina"

3.defaultHost="localhost"

4.jvmRoute="tomcat1">

<Engine

name="Catalina"

defaultHost="localhost"

jvmRoute="tomcat1">

另一个Tomcat设置改为

Xml代码

1.<Engine

2.name="Catalina"

3.defaultHost="localhost"

4.jvmRoute="tomcat2">

<Engine

name="Catalina"

defaultHost="localhost"

jvmRoute="tomcat2">

通过jvmRoute,指定了Tomcat唯一标识!

然后修改/etc/apache2/mods-available/proxy.conf

Shell代码

1.sudovi/etc/apache2/mods-available/proxy.conf

sudovi/etc/apache2/mods-available/proxy.conf

如下:

Java代码

1.<Proxybalancer://zlex>

2.BalancerMemberajp://localhost:8009/zlexroute=tomcat1

3.BalancerMemberajp://192.168.49.1:8009/zlexroute=tomcat2

4.</Proxy>

5.

6.ProxyPass/zlexbalancer://zlex

7.ProxyPassReverse/zlexbalancer://zlex

<Proxybalancer://zlex>

BalancerMemberajp://localhost:8009/zlexroute=tomcat1

BalancerMemberajp://192.168.49.1:8009/zlexroute=tomcat2

</Proxy>

ProxyPass/zlexbalancer://zlex

ProxyPassReverse/zlexbalancer://zlex

这里需要通过修改route属性,将Apache与Tomcat关联起来!

注意,Tomcat中定义的jvmRoute需要与Apache定义的route相对应!

我们来看一下http://localhost/balancer-manager发生了什么变化:

我们注意到route字段有了新的标识,当然,我们也可以通过这个配置界面修改这些信息,但当前修改不会真的修改/etc/apache2/mods-available/proxy.conf文件,Apache重启后将丢失。

为了更细致的对比进过复杂均衡的结果,这里增加了zlex应用!主要是监控Session的变化!

只看核心代码:

Jsp代码

1.<b>当前SessionID:</b>

2.<br/>

3.<%

4.StringsessionID=session.getId();

5.out.println(sessionID);

6.System.err.println("sessionid="+sessionID);

7.

8.//如果有新的Session属性设置

9.StringdataName=request.getParameter("dataName");

10.if(dataName!=null&&!dataName.isEmpty()){

11.StringdataValue=request.getParameter("dataValue");

12.session.setAttribute(dataName,dataValue);

13.}

14.%>

15.<br/>

16.<br/>

17.<b>Session属性列表:</b>

18.

19.<br/>

20.<%

21.Enumeration<String>e=(Enumeration<String>)session

22..getAttributeNames();

23.while(e.hasMoreElements()){

24.Stringname=e.nextElement();

25.Stringvalue=(String)session.getAttribute(name);

26.out.println(name+"="+value+"<br>");

27.System.err.println(name+"="+value);

28.}

29.%>

30.<formmethod="POST">

31.<ulstyle="list-style-type:none;">

32.<li><labelfor="dataName">键:</label><inputsize="20"id="dataName"

33.name="dataName"></li>

34.<li><labelfor="dataValue">值:</label><inputsize="20"

35.id="dataValue"name="dataValue"></li>

36.<li><inputtype="submit"value="提交"/></li>

37.</ul>

38.</form>

<b>当前SessionID:</b>

<br/>

<%

StringsessionID=session.getId();

out.println(sessionID);

System.err.println("sessionid="+sessionID);

//如果有新的Session属性设置

StringdataName=request.getParameter("dataName");

if(dataName!=null&&!dataName.isEmpty()){

StringdataValue=request.getParameter("dataValue");

session.setAttribute(dataName,dataValue);

}

%>

<br/>

<br/>

<b>Session属性列表:</b>

<br/>

<%

Enumeration<String>e=(Enumeration<String>)session

.getAttributeNames();

while(e.hasMoreElements()){

Stringname=e.nextElement();

Stringvalue=(String)session.getAttribute(name);

out.println(name+"="+value+"<br>");

System.err.println(name+"="+value);

}

%>

<formmethod="POST">

<ulstyle="list-style-type:none;">

<li><labelfor="dataName">键:</label><inputsize="20"id="dataName"

name="dataName"></li>

<li><labelfor="dataValue">值:</label><inputsize="20"

id="dataValue"name="dataValue"></li>

<li><inputtype="submit"value="提交"/></li>

</ul>

</form>

将其做成一个名为zlex的web应用,分别部署到两个Tomcat上!

然后重启Apache:

Shell代码

1.sudo/etc/init.d/apache2restart

sudo/etc/init.d/apache2restart

不断刷新http://localhost/zlex,看看真正的结果:

第1次:

第2次:

第3次:

第4次:

仔细观察,每次请求都按照负载均衡配置的节点次序依次请求到不同的Tomcat上。尤其是当我们通过jvmRoute和route做了绑定之后,信息更加准确。但是,仔细观察,每次请求的SessionID都是不一样!对于纯Web应用,尤其是依靠SessionID区分唯一用户的应用,这将是一场噩梦——解决了服务器压力均衡问题,却带来了SessionID不唯一问题!这就需要SessionID绑定,或者说叫做“会话复制”。

如果这时候你在页面上提交表单,将键值对保持在session中,在页面刷新后,将无法获得该信息,因为Seesion丢失了!

接着修改/etc/apache2/mods-available/proxy.conf,让SeesionID保持唯一:

Shell代码

1.sudovi/etc/apache2/mods-available/proxy.conf

sudovi/etc/apache2/mods-available/proxy.conf

增加stickySession属性:

Conf代码

1.ProxyPass/zlexbalancer://zlexstickySession=JSESSIONID

ProxyPass/zlexbalancer://zlexstickySession=JSESSIONID

stickySession粘性会话,根据这一属性,浏览器将通过cookie绑定SeesionID。如果这个时候再次访问http://localhost/zlex,你会发现,页面不会来回跳转了!

sticky是什么?

引用

sticky模式

利用负载均衡器的sticky模式的方式把所有同一session的请求都发送到相同的Tomcat节点。这样不同用户的请求就被平均分配到集群中各个tomcat节点上,实现负载均衡的能力。这样做的缺点是没有灾难恢复的能力。一旦一个节点发生故障,这个节点上所有的session信息全部丢失;

同一用户同一session只和一个webServer交互,一旦这个webserver发生故障,本次session将丢失,用户不能继续使用!

提交一个Session设定看看http://localhost/zlex:

观察后台日志:

再看看返回页面,这相当于一次页面刷新,如果正常粘性会话,我们将获得当前SessionID对应的一切信息:

这说明粘性会话生效了!

我们得到了形如

引用

50DAF14C6CDF8ACFBDC1095A5EE8E2CF.tomcat1

的SessionID。这样,我们就能知道当前访问的是哪台服务器了!

如果,换一个浏览器打开该页面http://localhost/zlex,将会获得一个新的SessionID,并且,根据Apache中配置的负载均衡节点列表依次访问下一个节点!

如果这时候负载均衡节点列表中某一节点发生异常,那么Apache将按照惯例,跳转该节点,并在该节点恢复正常后约1分钟内重新将其纳入可用节点!

修改刚才的jsp页面,看看Http头中都有些什么:

Jsp代码

1.<b>Cookie信息:</b>

2.<br/>

3.${header["cookie"]}

4.<br/>

5.<b>Host信息:</b>

6.<br/>

7.${header["host"]}

8.<br/>

<b>Cookie信息:</b>

<br/>

${header["cookie"]}

<br/>

<b>Host信息:</b>

<br/>

${header["host"]}

<br/>

sticky模式的根本在于浏览器支持cookie,如果浏览器不支持cookie,则需要修改server.xml文件中的<Context/>节点,将cookie置为false,关闭cookie功能,让jsessionid显式传递!

6.Tomcat集群,Session复制

经过两天反复研究,两只互不相认的Tomcat终于在网络上“资源共享”了——Session复制成功!

关于Tomcat集群以及Session复制,网上已经有很多很多,但是否真的能用?!为了确认这一问题,周末还跑到书店翻了翻《ApacheTomcat高级编程》,参考Clustering/SessionReplicationHOW-TO(有点小错误),经过两天苦战,克服种种小问题,终于拿下!

整理概念:

引用

群集,是包含多个服务器实例的指定集合,这些服务器实例共享相同的应用程序、资源以及配置信息。您可以将不同计算机上的服务器实例分组到一个逻辑群集中并将其作为一个单元来管理。您可以使用DAS轻松控制多机群集的生命周期。

群集可以实现水平可伸缩性、负载平衡和故障转移保护。根据定义,群集中的所有实例都具有相同的资源和应用程序配置。当群集中的服务器实例或计算机出现故障时,负载平衡器检测到该故障,会将通信从出现故障的实例重定向至群集中的其他实例,并恢复用户会话状态。由于群集中所有实例上的应用程序和资源都相同,因此一个实例可以故障转移至群集中的任何其他实例。

引用

Session复制,主要是指集群环境下,多台应用服务器之间同步Session,确保Session保持一致,且Session中的内容保持一致,对外透明——看起来就像是一台应用服务器!

如果其中一台服务器发生故障,根据负载均衡的原理,Apache会遍历寻找可用节点,分发请求。与此同时,当前用户Session不能发生数据丢失,其余各节点服务器应保证用户Session数据同步。

Session复制核心内容主要是:

1.Session内容序列化(serialize),会消耗系统性能。

2.Session内容通过广播同步给成员,会造成网络流量瓶颈,即便是内网瓶颈。

因此,Session复制的这两个潜在问题,致使复杂均衡节点最多不会超过4个。因为,当节点数大于4时,整个集群的吞吐量将不再上升!

为了搭建Tomcat集群,我将两个Tomcat分别部署到两台虚拟机上,确保网段一致。(这一步很关键,我最初将Tomcat1(192.168.49.132)部署在虚拟机上,将Tomcat2(192.168.49.128)部署在本机上,结果,网络总有问题,耽误了很多时间。)

由于变换了IP,我需要修改Apache的/etc/apache2/mods-available/proxy.conf文件:

Shell代码

1.sudovi/etc/apache2/mods-available/proxy.conf

sudovi/etc/apache2/mods-available/proxy.conf

修改负载均衡节点如下:

Conf代码

1.<Proxybalancer://zlex>

2.BalancerMemberajp://192.168.49.128:8009/zlexroute=tomcat1

3.BalancerMemberajp://192.168.49.132:8009/zlexroute=tomcat2

4.</Proxy>

<Proxybalancer://zlex>

BalancerMemberajp://192.168.49.128:8009/zlexroute=tomcat1

BalancerMemberajp://192.168.49.132:8009/zlexroute=tomcat2

</Proxy>

对于windows系统,不需要考虑网络问题,广播地址(这里用到224.0.0.0和240.0.0.0)默认开放,对于linux则需要通过命令开放地址。

Ubuntu上开放广播地址(eth0网卡):

Shell代码

1.sudorouteadd-net224.0.0.0netmask240.0.0.0deveth0

sudorouteadd-net224.0.0.0netmask240.0.0.0deveth0

然后通过-v参数查看当前开放的广播地址:

Shell代码

1.route-v

route-v

注意,重启后,该路由设置将丢失!

在Ubuntu下,可以考虑修改/etc/networks文件!

如果有必要,Windows上开放广播地址(192.168.49.128本机地址):

Cmd代码

1.routeadd224.0.0.0mask240.0.0.0192.168.49.128

routeadd224.0.0.0mask240.0.0.0192.168.49.128

然后通过print参数查看当前开放的广播地址:

Shell代码

1.routeprint

routeprint

然后,修改tomcat的server.xml文件:

Shell代码

1.sudovi/etc/tomcat6/server.xml

sudovi/etc/tomcat6/server.xml

在<Engine/>节点中加入如下内容:

Xml代码

1.<Clusterclassname="org.apache.catalina.ha.tcp.SimpleTcpCluster"

2.channelSendOptions="8">

3.

4.<Managerclassname="org.apache.catalina.ha.session.DeltaManager"

5.expireSessionsOnShutdown="false"

6.notifyListenersOnReplication="true"/>

7.

8.<Channelclassname="org.apache.catalina.tribes.group.GroupChannel">

9.<Membershipclassname="org.apache.catalina.tribes.membership.McastService"

10.address="224.0.0.0"

11.port="45564"

12.frequency="500"

13.dropTime="3000"/>

14.<Receiverclassname="org.apache.catalina.tribes.transport.nio.NioReceiver"

15.address="192.168.49.1"

16.port="4000"

17.autoBind="100"

18.selectorTimeout="5000"

19.maxThreads="6"/>

20.

21.<Senderclassname="org.apache.catalina.tribes.transport.ReplicationTransmitter">

22.<Transportclassname="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>

23.</Sender>

24.<Interceptorclassname="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>

25.<Interceptorclassname="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>

26.</Channel>

27.

28.<Valveclassname="org.apache.catalina.ha.tcp.ReplicationValve"

29.filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>

30.<Valveclassname="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

31.

32.<ClusterListenerclassname="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>

33.<ClusterListenerclassname="org.apache.catalina.ha.session.ClusterSessionListener"/>

34.</Cluster>

<Clusterclassname="org.apache.catalina.ha.tcp.SimpleTcpCluster"

channelSendOptions="8">

<Managerclassname="org.apache.catalina.ha.session.DeltaManager"

expireSessionsOnShutdown="false"

notifyListenersOnReplication="true"/>

<Channelclassname="org.apache.catalina.tribes.group.GroupChannel">

<Membershipclassname="org.apache.catalina.tribes.membership.McastService"

address="224.0.0.0"

port="45564"

frequency="500"

dropTime="3000"/>

<Receiverclassname="org.apache.catalina.tribes.transport.nio.NioReceiver"

address="192.168.49.1"

port="4000"

autoBind="100"

selectorTimeout="5000"

maxThreads="6"/>

<Senderclassname="org.apache.catalina.tribes.transport.ReplicationTransmitter">

<Transportclassname="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>

</Sender>

<Interceptorclassname="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>

<Interceptorclassname="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>

</Channel>

<Valveclassname="org.apache.catalina.ha.tcp.ReplicationValve"

filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>

<Valveclassname="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

<ClusterListenerclassname="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>

<ClusterListenerclassname="org.apache.catalina.ha.session.ClusterSessionListener"/>

</Cluster>

这里需要注意<Membership/>和Receiver:<Membership/>节点的address属性是广播地址;Receiver节点的address属性是本地绑定地址。当然,默认为auto。由于我在启动Tomcat时,Tomcat频频将地址指向127.0.0.1,无奈只好使用固定IP。

此外,为了降低Session复制的成本,Tomcat通过<Valve/>节点,以过滤器的方式控制哪些请求可以忽略Session复制:

Xml代码

1.<Valveclassname="org.apache.catalina.ha.tcp.ReplicationValve"

2.filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>

<Valveclassname="org.apache.catalina.ha.tcp.ReplicationValve"

filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>

同时,在<Host>节点中加入如下内容:

Xml代码

1.<Deployerclassname="org.apache.catalina.ha.deploy.FarmWarDeployer"

2.tempDir="/tmp/war-temp/"

3.deployDir="/tmp/war-deploy/"

4.watchDir="/tmp/war-listen/"

5.watchEnabled="false"/>

<Deployerclassname="org.apache.catalina.ha.deploy.FarmWarDeployer"

tempDir="/tmp/war-temp/"

deployDir="/tmp/war-deploy/"

watchDir="/tmp/war-listen/"

watchEnabled="false"/>

在Tomcat的官方文档(Tomcat6)中,对于<Deployer/>节点的部署位置是错误的,通过观察Tomcat启动日志,确认该节点应当不属于<Host/>节点中!

注意:Tomcat6与Tomcat5在上述节点中使用的类包(包中名称由cluster变化为ha)有所不同,且结构有所调整。

先别急着重启,我们需要修改应用中的web.xml文件,将<distributable/>节点部署到<web-app/>节点中,开启分布式服务:

注意:如果没有设置该节点,SessionID将不能保持同步,不同的服务器将各自建立独立的SessionID!

监控Tomcat日志:

Shell代码

1.tail-f/var/lib/tomcat6/logs/catalina.out

tail-f/var/lib/tomcat6/logs/catalina.out

然后重启Tomcat1:

Shell代码

1.sudo/etc/init.d/tomcat6restart

sudo/etc/init.d/tomcat6restart

观察日志:

注意两处红框:

第一处,Cluster启动,并绑定192.168.49.132:4000上,进行TCP通讯,并等待其它成员(Member)。

第二处,在管理器中注册/zlex,绑定JvmRouteBinderValve。

至此,说明集群设置已经生效,但不能说明集群配置成功!

接着我们启动Tomcat2,观察其日志:

Cluster启动,并绑定192.168.49.128:4000上并发现成员192.168.49.132!

再看Tomcat1的日志:

Tomcat1发现其成员Tomcat2!这说明TCP通讯已建立,Tomcat成员可以进行Session同步!

同时,Tomcat成员直接会每隔一个时间段相互侦测/验证其他成员是否正常:

现在,开始访问http://localhost/zlex,并不断刷新当前页面:

除了两处标识主机来源的tomcatX不同外,session是完全一致的!

提交一次修改,并不断刷新当前页:

如果仔细观察,当前SessionID在不断交替变化,这说明负载均衡在起作用!

我们再来看看2个Tomcat后台日志都做了什么!

Tomcat1:

Tomcat2:

两只猫都打印了相同的内容(a=1)不同的细节在于,sessionID带有服务器标识!

如果我们强行关闭Tomcat2:

首先,Tomcat1会很快侦测到Tomcat2离线,因为这是TCP通讯,成员之间很容易检测到其他成员是否离线!Tomcat1后台日志如下:

其次,Apache会侦测到Tomcat2发生异常,将其余请求转交给其他节点,即交由Tomcat1处理!

继续刷新http://localhost/zlex当前页面,耐心等待几秒。你会发现,即便再次刷新页面,sessionID仍旧绑定在标识tomcat1服务器上。

然后,我们恢复Tomcat2服务,Tomcat1会马上侦测到Tomcat2已经恢复正常:

最后,我们再次刷新当前页,Apache已经将请求分发给Tomcat2了,从后台日志可以看到session信息会很快被同步了!

如果带有tomcatX标识的sessionID有很多不便之处,可以关闭粘性会话。简单的讲,就是取消Tomcat中server.xml中<Engine/>节点的jvmRoute属性!然后,重启tomcat、apache!

页面提交一个b=3!

左边为Tomcat1,右边为Tomcat2!SessionID一致!

除了上述几种方案外,还有Terracotta模式。一种第三方集群组件,2009年收购了缓存组件EhCache,可以结合Tomcat、JBoss等多种服务器,提供多种负载均衡、集群等功能实现,且当负载均衡节点超过8个时,仍然能够保持集群吞吐量的线性增长。

Eclipse插件地址:

http://download.terracotta.org/eclipse/update

下载地址:

http://www.terracotta.org/dl/oss-download-catalog

至此,Apache+Tomcat成功完成,征服Apache系列暂告一段落!

作为开博以来的第100帖,算是很成功了!

测试应用见附件!

相关内容:

征服Apache+SSL

征服Apache+SVN

征服Apache+SVN+LDAP

征服Apache+Tomcat

征服Nginx

征服Nginx+Tomcat

相关推荐