tomcat集群--整合Apache服务器,采用mod_JK连接模式

时间:2021-11-21 13:28:21

一、集群技术初步分析

1、tomcat集群多应用服务器,分担压力,并保证服务高可用,使用tomcat自带cluster实现集群环境,Manager负责管理session共享
2、web服务器采用Apache,负责应用服务器间负载均衡
3、apache服务器和tomcat的连接方法其实有三种:mod_JK、http_proxy和ajp_proxy,本文采用mod_JK模式
4、盗一张图,如下:
tomcat集群--整合Apache服务器,采用mod_JK连接模式

二、tomcat集群环境安装与配置

1、下载tomcat服务器,本文使用apache-tomcat-7.0.64版本,复制两份,分别为tomcat1和tomcat2,配置server.xml,以tomcat1为例,配置代码如下:

<!-- 服务关闭端口 -->
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
-->

<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->

<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />

</GlobalNamingResources>

<!-- A "Service" is a collection of one or more "Connectors" that share
a single "Container" Note: A "Service" is not itself a "Container",
so you may not define subcomponents such as "Valves" at this level.
Documentation at /docs/config/service.html
-->

<Service name="Catalina">

<!-- http连接器使用nio非阻塞模式,Excutor作为执行器提供线程池支持 -->
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="150" minSpareThreads="10"/>


<!-- 负责建立HTTP连接,使用nio非阻塞模式,配置使用executor执行器,使用连接池 -->
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
redirectPort="8443"
executor="tomcatThreadPool"/>


<!-- Define an AJP 1.3 Connector on port 8009 -->
<!-- AJP连接器,监听8009端口,采用AJP协议,连接tomcat服务器与http服务器(如Apache服务器),实现两者通信 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->

<!-- 使用AJP协议实现负载均衡,需要jvmRoute参数,值自定义
Tomcat会在创建session时会根据根据jvmRoute的值在sessionID后面追加route值
用于追踪分发的请求
-->

<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">

<!-- Cluster:配置管理集群节点,主要组件包括:Manager、Channel、Valve、ClusterListenner等
其中,Manager用于管理session会话信息;Channel作为连接各个节点的通信通道,管理连接各个节点;ClusterListenner监听器,监听Cluster组件接收的消息,并把信息传递给Manager;Valve过滤器或阀门

Cluster节点属性:
className:Cluster的实现类
channelSendOptions:可以设置为2、4、8、10,每个数字代表一种方式
2 = Channel.SEND_OPTIONS_USE_ACK(确认发送)
4 = Channel.SEND_OPTIONS_SYNCHRONIZED_ACK(同步发送)
8 = Channel.SEND_OPTIONS_ASYNCHRONOUS(异步发送)
10 = 在异步模式下,可以通过加上确认发送(Acknowledge)来提高可靠性
-->

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">

<!-- session manager -->
<!--
Manager:决定如何管理集群的Session信息,集群环境下实现有两种:DeltaManager和BackupManager,经常被使用的是DeltaManager
DeltaManager,它会将所有的会话的改变同步给集群中的每一个节点
BackupManager: 某节点会话的改变只会同步给集群中的另外一个或者几个节点,而不是所有节点,只用于会话备份

Manager节点属性:
className:Manager的实现类
expireSessionsOnShutdown:设置为true时,一个节点关闭,将导致集群下的所有Session失效
notifyListenersOnReplication:集群下节点间的Session复制、删除操作时,是否异步通知Cluster Listener
maxInactiveInterval:集群下Session的有效时间(单位:s)。
maxInactiveInterval:内未活动的Session,将被Tomcat回收。默认值为1800(30min)
-->

<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>


<!--
Channel:集群各个节点之间进行通讯通道,管理连接各个节点;
是一个抽象的端口,和socket类似,集群各个节点通过它收发信息。
Channel包括4个组件:Membership、Receiver、Sender、Interceptor(可选)

-->

<Channel className="org.apache.catalina.tribes.group.GroupChannel">

<!-- Membership:多播通道,维护集群的可用节点列表;
所有节点这个配置是相同的;
它可以检查到新增的节点,也可以检查到没有心跳的节点;
集群中各个节点通过组播地址和组播端口号注册到多播通道中,来实现由membership管理各个节点的效果

Membership节点属性:
address:组播地址
port:组播端口
frequency:发送心跳(向组播地址发送UDP数据包)的时间间隔(单位:ms)。默认值为500
dropTime:Membership在dropTime(单位:ms)内未收到某一节点的心跳,则将该节点从可用节点列表删除。默认值为3000
-->

<Membership className="org.apache.catalina.tribes.membership.McastService"
address="228.0.0.4"
port="45564"
frequency="500"
dropTime="3000"/>


<!--
Reciver:接收器,用于Channel中各个节点如何从其他节点的Sender接收复制数据,有 两种实现模式:阻塞模式和非阻塞模式
1)BioReceiver( org.apache.catalina.tribes.transport.bio.BioReceiver )
2)NioReceiver( org.apache.catalina.tribes.transport.nio.NioReceiver )

Reciver节点属性:
address:接收消息所使用的地址,可以理解为消费者注册的地址
port:接收消息所使用的端口号,多个tomcat节点在一台物理服务器上注意要使用不同的端口
autoBind:端口的变化区间,如果port为4000,autoBind为100,接收器将在4000-4099间取一个端口,进行监听
selectorTimeout:Receiver内轮询监听的超时时间
maxThreads:线程池的最大线程数
-->


<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4001"
autoBind="100"
selectorTimeout="100"
maxThreads="6"/>


<!--
Sender:发送器,负责发送复制消息,同步给其它节点,实质负责发送消息功能的是内嵌的Transport组件

-->

<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<!--
Transport有两种实现模式:
org.apache.catalina.tribes.transport.bio.PooledMultiSender(阻塞式)、
org.apache.catalina.tribes.transport.nio.PooledParallelSender(非阻塞式)
-->

<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>

<!-- Interceptor : Cluster的拦截器 -->
<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:阀门、过滤器,用于在节点向客户端响应前进行检测或进行某些操作

ReplicationValve:用于检测当前的响应是否涉及Session数据的更新,如下面配置的filter,即当对静态页面图片等访问时不进行session replication
-->

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter=".*/.gif;.*/.js;.*/.jpg;.*/.png;.*/.htm;.*/.html;.*/.css;.*/.txt;"/>

<!--JvmRouteBinderValve:当一个节点crash时,访问跳到另一个节点,此时session ID 会将jvmRoute值和以前的session Id 绑定在一起 -->
<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 : 监听器,监听Cluster组件接收的消息,并把信息传递给Manager
使用DeltaManager时,Cluster接收的信息通过ClusterSessionListener传递给DeltaManager
-->

<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>

<!-- Use the LockOutRealm to prevent attempts to guess user passwords
via a brute-force attack -->

<Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->

<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>

</Realm>

<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">


<!-- SingleSignOn valve, share authentication between web applications
Documentation at: /docs/config/valve.html -->

<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
-->


<!-- Access log processes all example.
Documentation at: /docs/config/valve.html
Note: The pattern used is equivalent to using pattern="common" -->

<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />


</Host>
</Engine>
</Service>
</Server>

如果只是实现集群负载均衡,上面的cluster是可以不配置,cluster的作用是实现集群高可用,也就是管理session信息共享

补充配置:

<!-- 如果多个tomcat部署在同一台机器上,服务关闭端口应该不同,比如tomcat2的配置由8005更改为9005 -->
<Server port="9005" shutdown="SHUTDOWN">

此外还有如下配置:

<!-- http连接端口以及请求转发端口,8080更改为8090,8443更改为9443 -->
<Connector port="8090" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
redirectPort="9443"
executor="tomcatThreadPool"/>
<!-- AJP连接器端口,由8009更改为9009,8443更改为9443 -->
<Connector port="9009" protocol="AJP/1.3" redirectPort="9443" />
<!-- 实现session信息追踪的配置,在tomcat2中,jvmRoute="tomcat1" -->
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">
<!-- 接收器使用的端口,由4001更改为5001 -->
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4001"
autoBind="100"
selectorTimeout="100"
maxThreads="6"/>

注意:上面所涉及到的端口,别忘记修改防火墙iptables开放端口,否则出现无法访问。

2、修改项目的 WEN-INF/web.xml

<web-app></web-app>之间添加:

<distributable/>

3、启动tomcat服务,测试是否可以访问

三、安装配置Apache服务器

1、下载Apache 2.0.25,官网地址:http://httpd.apache.org,下载地址:点击下载Apache 2.0.25
下载之后的文件:apache_2.0.55-win32-x86-no_ssl.msi,双击安装运行。
安装过程中,Network Domain 和 Server Name填写内容,如果用于本机测试,可以直接localhost,否则填写远程主机地址。
或者点击:补充下载地址

2、下载并配置mod_jk.so
因为采用的是mod_jk模式,需要下载mod_jk.so插件,mod_jk-apache-2.0.55.so
将下载的mod_jk-apache-2.0.55.so文件拷贝到Apache安装目录的modules目录下面

3、修改apache配置文件conf/http.conf

开放并修改:

ServerName localhost:80

在文件最后加上下面一句代码:

include conf/mod_jk.conf

4、配置mod_jk.conf
在http.conf同目录下,创建mod_jk.conf文件,内容如下:

#加载 mod_jk 模块
LoadModule jk_module modules/mod_jk-apache-2.0.55.so
#加载mod_jk.so 配置文件
JkWorkersFile conf/workers.properties
#指定那些请求交给tomcat处理,"controller"为在workers.propertise里指定的负载分配控制器
JkMount /* controller

5、配置workers.properties
在http.conf同目录下,创建workers.properties文件 ,内容如下:

###server 列表
###jvm1 需要注意此名字对应tomcat中server.xml<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat1">
worker.list = controller,tomcat1,tomcat2
###tomcat1
###ajp13端口号,在tomcat下server.xml配置,默认8009
worker.tomcat1.port=8009
worker.tomcat1.host=192.168.0.10
worker.tomcat1.type=ajp13
###server的加权比重,值越高,分得的请求越多
worker.tomcat1.lbfactor=1

###tomcat2
worker.tomcat2.port=9009
worker.tomcat2.host=192.168.0.10
worker.tomcat2.type=ajp13
worker.tomcat2.lbfactor=1

###controller
worker.controller.type=lb
###指定分担请求的tomcat
worker.controller.balance_workers=tomcat1,tomcat2
worker.controller.sticky_session=1

6、启动Apache服务

四、测试集群环境

1、tomcat1、tomcat2中,项目分别创建两个页面:session_set.jsp和session_get.jsp

tomcat1中
session_set.jsp:

<%@ page language="java" pageEncoding="UTF-8"%>
<%
request.getSession().setAttribute("name", "tomcat1");
System.out.println("tomcat1 set attribute success!");
%>

<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
tomcat1 set attribute success!<br>
</body>
</html>

session_get.jsp:

<%@ page language="java" pageEncoding="UTF-8"%>
<%
Object name = request.getSession().getAttribute("name");
System.out.println("tomcat1 get attribute:"+name);
%>

<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
tomcat1 get attribute:<%=name %><br>
</body>
</html>

tomcat2中
session_set.jsp:

<%@ page language="java" pageEncoding="UTF-8"%>
<%
request.getSession().setAttribute("name", "tomcat2");
System.out.println("tomcat2 set attribute success!");
%>

<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
tomcat2 set attribute success!<br>
</body>
</html>

session_get.jsp:

<%@ page language="java" pageEncoding="UTF-8"%>
<%
Object name = request.getSession().getAttribute("name");
System.out.println("tomcat2 get attribute:"+name);
%>

<!DOCTYPE html>
<html>
<head>
<title>test</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
tomcat2 get attribute:<%=name %><br>
</body>
</html>

负载均衡测试:
启动tomcat1、tomcat2、Apache服务之后,用不同的浏览器多次访问 http://localhost/test/session_set.jsp
http://localhost/test/session_get.jsp
查看结果是否请求被分发到tomcat1和tomcat2上面,如果是,则实现负载均衡。

高可用测试:
负载均衡测试完毕之后,关闭tomcat2,然后刷新会话在tomcat2上面的session_get.jsp页面,查看是否能拿到name的值为tomcat2,如果是,则说明tomcat2的会话信息同步到了tomcat1中,实现了高可用。

关于高可用session不能复制或复制失败的处理:
1、Receiver接收器这个配置:autoBind=”100”,尽量不配(不过默认值好像就是这个)
2、还是接收器配置:address=”auto”,尽量指定ip地址
3、防火墙开启端口:8005、9005、8080、8090、8009、9009、8443、4001、5001、45564等上面tomcat配置所涉及的端口
4、如果执行上述操作,不起作用,关闭防火墙,重启tomcat集群,测试;成功后,开启防火墙,再测

文章参考:
http://blog.csdn.net/yipanbo/article/details/44079083/
http://blog.csdn.net/zhuying_linux/article/details/6590700