4. Tomcat + httpd(mod_jk)
使用mod_jk模块和tomcat连接时,tomcat的连接器一般都使用ajp协议类型。
mod_jk不是apache httpd的原生模块,而是类似于第三方模块,因此需要额外编译mod_jk模块到httpd中,就像将php模块添加到httpd中一样。
4.1 编译mod_jk模块
当前最新稳定版的mod_jk是1.2.42版本。
mod_jk下载地址:http://tomcat.apache.org/download-connectors.cgi。
mod_jk官方手册:http://tomcat.apache.org/connectors-doc/。
httpd要扩展模块需要借助apxs,它是httpd的开发包httpd-devel中工具,所以先要安装httpd-devel。如果是编译安装的httpd,则devel包已经装好,如果是yum安装,则需要额外安装httpd-devel包。
此处为了方便,httpd使用yum安装。所以编译mod_jk的方式如下:
yum -y
install httpd httpd-devel
tar xf tomcat-connectors-1.2.42-src.tar.gz
cd tomcat-connectors-1.2.42-src/native/
./configure --with-apxs=/usr/bin/apxs --prefix=/usr/local/tomcat/mod_jk
make && make install
4.2 配置httpd与tomcat的ajp连接
此处暂先配置httpd与其中一个tomcat(192.168.100.22)连接。后文在说明负载均衡时再引入另一个tomcat。
先提供一个额外的httpd配置文件。
[root
@xuexi~]
# cat /etc/httpd/conf.d/mod_jk.conf
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/conf.d/workers.properties
JkLogFile logs/mod_jk.
log
JkLogLevel debug
######### "JkMount /* TomcatA" will send all request to TomcatA ########
JkMount /*.jsp TomcatA
JkMount /status/* statA
JkUnMount /images/* TomcatA
JkUnMount /css/*.* TomcatA
JkUnMount /css_js/* TomcatA
JkUnMount /*.html TomcatA
JkUnMount /*.js TomcatA
mod_jk的配置文件官方手册:http://tomcat.apache.org/connectors-doc/reference/apache.html。以下是几个常用的指令说明。
-
LoadModule
指令用于装载mod_jk相关模块,除此之外还需要在httpd的配置文件中设置其它一些指令来配置其工作属性。如: -
JkWorkersFile
用于指定保存了worker相关工作属性定义(见下文)的配置文件。 -
JkLogFile
用于指定mod_jk模块的日志文件。 -
JkLogLevel
用于指定日志级别(info,error,debug),此外还可以使用JkRequestLogFormat自定义日志信息格式。 -
JkMount
(格式:JkMount )则用于控制URL与Tomcat workers的对应关系。可以理解为转发请求的意思,例如"/status/*"表示url地址后加上/status/可转发至statA这个worker上。注意,JkMount匹配的URL是相对的。如果JkMount指令放在Location指令中,如<Location /app>
,则JkMount将从/app的后面开始匹配。
JkMount和JkUnMount是很重要的指令,mod_jk性能之所以比mod_proxy好,就是因为通过这两个指令可以实现动静分离,使得只将动态请求转发给tomcat。其中JkMount指定要转发给tomcat处理的请求,JkUnMount指定明确不转发给tomcat而是在本地处理的请求。虽然不指定JkUnMount时,也表示不转发给tomcat,但如果有重叠时,则应该指定JkUnMount。例如下面的例子,除了/myapp/下的js文件,其他都转发给tomcat1处理。
JkMount /myapp/* tomcat1
JkUnMount /myapp/
*.jstomcat1
对于apache来说,每一个后端Tomcat实例中的engine都可以视作一个worker,而每一个worker的地址、Connector的端口等信息都需要在apache端指定以便可以识别并使用这些worker。配置这些信息的文件通常为"workers.properties",其具体路径是使用前面介绍过的JkWorkersFile指定的。在apache启动时,mod_jk会扫描此文件获取每一个worker配置信息。如这里使用/etc/httpd/conf.d/workers.properties
。
workers.properties文件一般由两类指令组成:一是mod_jk可以连接的各worker名称列表,二是每一个worker的属性配置信息。详细的配置方法见官方手册:http://tomcat.apache.org/connectors-doc/reference/workers.html。
以下是和上述/etc/httpd/conf.d/mod_jk.conf中配置相对应的/etc/httpd/conf.d/workers.properties。
[root
@xuexitomcat]
# cat /etc/httpd/conf.d/workers.properties
worker.list=TomcatA,statA
worker.TomcatA.type=ajp13
worker.TomcatA.host=
192.168.
100.22
worker.TomcatA.port=
8009
worker.TomcatA.lbfactor=
1
worker.statA.type = status
关于worker的配置,它们分别遵循如下使用语法。
worker.list =
<a comma separated list of worker_name>
worker.
<worker_name>.
<property>=
<property value>
其中worker.list指令可以重复指定多次。worker_name是Tomcat中engine组件中jvmRoute属性的值(jvmRoute可以不指定,此时worker_name仅用于标识worker)。
根据工作机制的不同,worker有多种不同的类型,每个worker都需要指定其类型,即设定woker..type项。常见的类型如下:其中ajp13是默认值。
- ajp13:此类型是web server和tomcat首选的类型。此外,还有ajp12和ajp14,但它们一个废弃一个处于测试阶段。
- lb:lb用于负载均衡场景中的worker;此worker并不真正负责处理用户请求,而是将用户请求调度给其它类型为ajp13的worker。
- status:用户显示负载均衡中各worker工作状态的特殊worker,它不处理任何请求,也不关联到任何实际工作的tomcat实例。
由于status是状态监控页面,所以应该保证其安全性,可以在httpd的配置文件中加入以下控制列表:
# 注意,必须加上尾随斜线,因为在mod_jk.conf中已经明确了"/status/*"
# For http 2.2
<Location /status/>
Order
deny,allow
Deny
from
all
Allow
from 192.168.100.0/24
</Location>
# For http 2.4
<Location /status/>
Requrie
ip 192.168.100
</Location>
除了type属性外,worker其它常见的属性有:
除了type属性外,worker其它常见的属性有:
- host:worker所在的主机,更具体的是tomcat上connector组件设置的ajp监听地址;
- port:worker的AJP1.3连接器监听的端口;
- connection_pool_minsize:最少要保存在连接池中的连接的个数;默认为pool_size/2;
- connection_pool_timeout:连接池中连接的超时时长;
- mount:由当前worker提供的context路径,如果有多个则使用空格格开;可考虑在httpd端使用JkMount替代。
- retries:错误发生时的重试次数;
- socket_timeout:mod_jk等待worker响应的时长,默认为0,即无限等待;
- socket_keepalive:是否启用keep alive的功能,1表示启用,0表示禁用;
- lbfactor:worker的权重,可以在负载均衡的应用场景中为worker定义此属性;
另外,在负载均衡模式中专用的属性还有:
- balance_workers:用于负载均衡模式中的各worker的名称列表,需要注意的是,出现在此处的worker名称一定不能在任何worker.list属性列表中定义过,并且worker.list属性中定义的worker名字必须包含负载均衡worker。
- method:可以设定为R、T或B;默认为R,即根据请求的个数进行调度(wrr);T表示根据已经发送给worker的实际流量大小进行调度;B表示根据实际负载情况进行调度(leastconn)。
- sticky_session:将某请求调度至某worker后,此地址后续所有请求都将直接调度至此worker,实现将用户session与某worker绑定。默认为值为true,即启用此功能。如果后端的各worker之间支持session复制,则可设为false。
至此,一个基于mod_jk模块与后端名为TomcatA的worker通信的配置已经完成,重启httpd服务即可生效。
测试:在浏览器中输入
http:
//192.168.100.17/
http:
//192.168.100.17/index.jsp
http:
//192.168.100.17/status/
如果都能获取页面,则表示apache通过mod_jk和tomcat基于ajp协议类型的连接已经成功。
4.3 通过mod_jk负载均衡tomcat
使用mod_jk实现tomcat的负载均衡有一个好处,tomcat上可以禁用http协议(将监听此协议的Connector配置删除即可),防止外界直接通过http请求tomcat。
配置apache,使其支持负载均衡,修改/etc/httpd/conf.d/mod_jk.conf为如下内容:
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/conf.d/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel
notice
JkMount /
*.jspTomcatLB
JkMount /status/* statA
编辑/etc/httpd/conf.d/workers.properties,修改为如下内容:为测试负载效果,不启用stick_session。
worker.
list=TomcatLB,statA
worker.statA.type=status
worker.TomcatLB.type=lb
worker.TomcatLB.sticky_session=
false
worker.TomcatLB.balance_workers=TomcatA,TomcatB
worker.TomcatA.type=ajp13
worker.TomcatA.host=
192.168.100.22
worker.TomcatA.port=
8009
worker.TomcatA.lbfactor=
5
worker.TomcatB.type=ajp13
worker.TomcatB.host=
192.168.100.23
worker.TomcatB.port=
8009
worker.TomcatB.lbfactor=
10
在mod_jk负载均衡中,后端tomcat的engine组件需要添加jvmRoute参数,该参数会为当前server实例设置全局惟一标识符,因此每一个实例的jvmRoute的值均不能相同,且jvmRoute的值必须等于balance_workers的成员值。对于上面的配置,Engine应该如下设置:此处还修改了name,但这不是要求要修改的。
<!-- 在tomcatA上设置 -->
<Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatA">
<!-- 在tomcatB上设置 -->
<Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatB">
为了演示效果,在TomcatA部署一个应用程序test。
[root
@xuexitomcat]
# mkdir -p /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
添加index.jsp,内容如下:
[root@xuexi tomcat]# cat /usr/local/tomcat/webapps/test/index.jsp
<%@ page language="java" %>
<html>
<head><title>
TomcatA
</title></head>
<body>
<h1><font color="red">
TomcatA
</font></h1>
<table align="centre" border="1">
<tr>
<td>
Session ID
</td>
<% session.setAttribute("abc","abc"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>
Created on
</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>
在TomcatB同样也部署一个应用程序test。如下:
[root@xuexi tomcat]# mkdir -p /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
[root@xuexi tomcat]# cat /usr/local/tomcat/webapps/test/index.jsp
<%@ page language="java" %>
<html>
<head><title>
TomcatB
</title></head>
<body>
<h1><font color="blue">
TomcatB
</font></h1>
<table align="centre" border="1">
<tr>
<td>
Session ID
</td>
<% session.setAttribute("abc","abc"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>
Created on
</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>
重启httpd、tomcatA、tomcatB对应的服务程序。在浏览器中输入192.168.100.17/test/index.jsp测试负载均衡是否生效。
测试时,轮调两次tomcatB后轮调一次tomcatA。而且可以发现每次轮询时Session ID每次都是变化的,因为没有开启sticky_session,所以session没有进行绑定。
要绑定会话,将worker.properties中的sticky_session设置为true即可。