最近做一个网站项目,需要考虑tomcat负载均衡问题,以下是我的总结.
1 环境
Tomcat6(4个)
Apache2.2
mod_jk-1.2.30-httpd-2.2.3.so
2 原理简述
Apache是http服务器,负责接收http请求。如何将请求分发给后台tomcat 呢,这就需要mod_jk了,mod_jk是Apache用来链接tomcat的模块。它需要配置在Apache中。下面描述的是他们各自的分工情况
Apache要做的:Apache默认处理接收到的所有http请求,但可通过配置将请求转发给某个模块处理,并将模块处理结果返回,在这里我们将所有请求都转发给mod_jk去处理。
Mod_jk要做的:
a) 登记所有的tomcat服务器,并针对不同功能划分的tomcat做不同的配置。
b) 将从Apache接收到的请求按策略分配给tomcat。分配策略如下:
i. 对于新的会话按配置比例分发给tomcat
ii. 对于已有会话(http中包含session信息)的请求会发送给创建该会话的tomcat
iii. 当创建会话的tomcat无法响应时,发送给该tomcat指定的备份tomcat。如果没有指定,则视为新会话处 理。
Tomcat要做的:处理mod_jk发送过来的请求并返回结果。之前提到如果某一tomcat无法响应转由其他tomcat处理时,原有的session信息将会丢失。为解决此问题,于是就有了session同步的概念,即多个服务器之间共享session,当某一服务器不同提供服务时不会导致用户session丢失。Tomcat的session共享是基于ip组播协议实现的。(http://blog.csdn.net/wu_jietian/archive/2010/04/13/5480010.aspx )这里只需要知道session同步是可以通过配置实现的,并且是由Tomcat实现的而非mod_jk。至于具体细节请参考另一篇文章(http://blog.csdn.net/wu_jietian/archive/2010/04/13/5479990.aspx )
了解了原理之后,配置起来就水到渠成了。
3 约束
代码必须符合以下几点要求:
1.session 中的 attribute必须继承java.io.Serializable
2.程序中如果存在全局变量,需要考虑多个Tomcat间变量同步的问题。
4 部署
a) 安装tomcat、apache2.2 略。 网上资料大把,不再重复。
注意事项:apache需要设置最大并行连接数,不同的操作系统配置的方式不同,因为不同的系统使用的是不同的多路处理模块。配置视情况而定。
b) 安装并配置mod_jk
i. 安装mod_jk:
把下载的 mod_jk-1.2.28-httpd-2.2.3.so 改名为 mod_jk2.so 放到apache的modules目录下。
修改apache的conf目录下的httpd.conf,在文件最后加入一行include conf/mod_jk2.conf,在conf目录下创建mod_jk2.conf文件,内容如下:
# 加载 mod_jk Module
LoadModule jk_module modules/mod_jk-1.2.30-httpd-2.2.3.so
# 指定 workers.properties 文件路径
JkWorkersFile conf/workers.properties
# 指定 log 目录
JkLogFile logs/mod_jk2.log
# 指定那些请求交给 tomcat 处理 ,"controller" 为在 workers.propertise 里指定的负载分配控制器
JkMount /* controller
ii.配置mod_jk:
worker.list = controller,tomcat4,tomcat3,tomcat2,tomcat1
#server 列表 tomcat1-tomcat4 命名必须与 tomcat 配置文件 server.xml 中的 jvmRoute 相对应
#========tomcat1========
worker.tomcat1.port=8009
#ajp13 端口号,在 tomcat 下 server.xml 配置 , 默认 8009
worker.tomcat1.host=localhost
#tomcat 的主机地址,如不为本机,请填写 ip 地址
worker.tomcat1.type=ajp13
worker.tomcat1.lbfactor = 1
#server 的加权比重,值越高,分得的请求越多
worker.tomcat1.redirect = tomcat2
# 指定 tomcat1 无法提供服务后由 tomcat2 继续提供服务
#========tomcat2========
worker.tomcat2.port=8010
#ajp13 端口号,在 tomcat 下 server.xml 配置 , 默认 8009
worker.tomcat2.host=localhost
#tomcat 的主机地址,如不为本机,请填写 ip 地址
worker.tomcat2.type=ajp13
worker.tomcat2.lbfactor = 1
#server 的加权比重,值越高,分得的请求越多
worker.tomcat2.redirect = tomcat1
# 指定 tomcat2 无法提供服务后由 tomcat1 继续提供服务
worker.tomcat2.activation = d
# 指定 tomcat2 只接收属于它的 session 的请求,隐含意思是新的请求不会发给 tomcat2 。
# 如果 tomcat1 于 tomcat2 实现 session 共享且 tomcat1 挂掉时, tomcat2 可以且只能为 session 属于 tomcat1 的请求提供服务。(可能比较绕,需要费几个脑细胞)
#========tomcat3========
worker.tomcat3.port=8011
#ajp13 端口号,在 tomcat 下 server.xml 配置 , 默认 8009
worker.tomcat3.host=localhost
#tomcat 的主机地址,如不为本机,请填写 ip 地址
worker.tomcat3.type=ajp13
worker.tomcat3.lbfactor = 1
#server 的加权比重,值越高,分得的请求越多
worker.tomcat3.redirect = tomcat4
#========tomcat4========
worker.tomcat4.port=8012
#ajp13 端口号,在 tomcat 下 server.xml 配置 , 默认 8009
worker.tomcat4.host=localhost
#tomcat 的主机地址,如不为本机,请填写 ip 地址
worker.tomcat4.type=ajp13
worker.tomcat4.lbfactor = 1
#server 的加权比重,值越高,分得的请求越多
worker.tomcat4.redirect = tomcat3
worker.tomcat4.activation = d
#========controller, 负载均衡控制器 ========
worker.controller.type=lb
worker.controller.balanced_workers=tomcat4,tomcat3,tomcat2,tomcat1 # 指定分担请求的 tomcat
worker.controller.sticky_session=1
c) 配置tomcat
i. <Server port="8005" shutdown="SHUTDOWN"> 如 果是同一台电脑,port不能重复
ii. <Engine name="Catalina" defaultHost="localhost"> 改为<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1-4">
iii. 设置session共享,Engine节点下插入以下配置:
<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"
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>
Port需要特殊说明:address 和port都相同的tomcat视为一个集群,session共享是发生在集群内部的,因此这里我们 将tomcat1,2为一组及port=45564,tomcat3,4为一组port=45565。
iv. 在tomcat中添加应用testGroup,在其根目录下增加index.jsp文件,index.jsp内容如下
<%
session.setAttribute("session1","session1");
//tomcat2 的则改为 session.setAttribute("session2","session2"); 以此类推
System.out.println("===========================");
System.out.println(session.getAttribute("session1"));
System.out.println(session.getAttribute("session2"));
System.out.println(session.getAttribute("session3"));
System.out.println(session.getAttribute("session4"));
System.out.println("sessionId = " + session.getId());
%>
<html>
<body >
<center>
<h1>Tomcat 1</h1>
<!--tomcat2 的则改为 Tomcat 2 以此类推 -->
</body>
</html>
v.在web.xml加入 <distributable/> 即可
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<distributable/>
<display-name>testGroup</display-name>
</web-app>
5 测试
启动tomcat1-4 再启动apache
a)反复重启浏览器访问http://127.0.0.1/testGroup 你会发现只有tomcat1和tomcat3 接收到的请求。(注意tomcat控制台打印信息)
b) 访问http://127.0.0.1/testGroup ,比如当前页面显示是tomcat1,此时关闭tomcat1,刷新页面,页面应该显示为tomcat2.并且tomcat2后台打印信息应为
==========
session1
session2
null
null
你会发现tomcat1,2之间实现了session共享
c)之后反复重启浏览器访问,你会发现页面显示的都是tomcat3,因为tomcat1挂了,tomcat2,4不接受新请求
总结完毕