[root@node2 ~]# rpm -e httpd
实验环境
node1 192.168.139.2
node4 192.168.139.8
node5 192.168.139.9
node1上编译安装httpd,用来作为前端反向代理服务器
node4|node5上安装Tomcat,用来作为后端的Tomcat应用程序服务器
node4安装jdk
[root@node4 tool]# tar -xf jdk-8u111-linux-x64.tar.gz -C /usr/local/java/
[root@node4 tool]# vim /etc/profile.d/java.sh
export JAVA_HOME=/usr/local/java/jdk1.8.0_111
export JRE_HOME=/usr/local/java/jdk1.8.0_111/jre
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
export PATH=$JAVA_HOME/bin:$PATH
[root@node4 tool]# . /etc/profile.d/java.sh
[root@node4 tool]# java -version
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
node4vim 安装Tomcat
[root@node4 tool]# tar -xf apache-tomcat-7.0.73.tar.gz -C /usr/local/
[root@node4 tool]# cd /usr/local/
[root@node4 local]# ln -sv apache-tomcat-7.0.73/ tomcat
`tomcat' -> `apache-tomcat-7.0.73/'
[root@node4 local]# cd tomcat/
[root@node4 tomcat]# vim /etc/profile.d/tomcat.sh
export CATALINA_HOME=/usr/local/tomcat
export PATH=$PATH:$CATALINA_HOME/bin
[root@node4 tomcat]# . /etc/profile.d/tomcat.sh
[root@node4 tomcat]# catalina.sh version
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr/local/java/jdk1.8.0_111/jre
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Server version: Apache Tomcat/7.0.73
Server built: Nov 7 2016 21:27:23 UTC
Server number: 7.0.73.0
OS Name: Linux
OS Version: 2.6.32-573.el6.x86_64
Architecture: amd64
JVM Version: 1.8.0_111-b14
JVM Vendor: Oracle Corporation
[root@node4 tomcat]# catalina.sh start
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /usr/local/java/jdk1.8.0_111/jre
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Tomcat started.
[root@node4 logs]# catalina.sh stop
添加一个虚拟主机,修改默认虚拟主机
[root@node4 logs]# vim /usr/local/tomcat/conf/server.xml
<Engine name="Catalina" defaultHost="node4.zxl.com" jvmRoute="TomcatA">
修改默认的虚拟主机为新添加的虚拟主机,并加一个jvm路由标签“TomcatA”(Jvm的路由名称,每个JVM实例都可以有一个独立的名称,用来在多JVM实例的情况下区分)
加入虚拟主机,其中应用程序目录为/web/webapps
<Host name="node4.zxl.com" appBase="/web"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="webapps" reloadable="true" />
</Host>
[root@node4 logs]# catalina.sh configtest #测试配置语法前,关掉tomcat
.......输出一大堆内容,不过没关系
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Jan 03, 2017 1:35:50 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 2315 ms
添加jsp测试页面
[root@node4 logs]# vim /web/webapps/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>
启动Tomcat
[root@node4 logs]# catalina.sh start
[root@node4 jsp]# cd /usr/local/tomcat/work/Catalina/node4.zxl.com/_/org/apache/jsp/
[root@node4 jsp]# ls
index_jsp.class index_jsp.java
注:其中work目录为工作目录,Catalina为Engine名称,node4.zxl.com为Host的名称
/usr/local/tomcat/work/Catalina/node4.zxl.com/_/org/apache/jsp/下的.class和.java就是我们刚才通过浏览器进行第一次访问/web/webapps/index.jsp时在tomcat的Web Container中动态编译生成的.java源程序和.class类,而第二次访问相同的资源,就直接使用这里的.class类,不用再次编译,这就是为什么用JSP写的站点第一次访问慢,第二次访问相同资源就很快的原因;同时也是用JSP写的站点访问快的主要原因
ok!在node4上tomcat已近安装并且测试成功,同样方法在node5安装Tomcat就行,这里不再演示
node5上的tomcat测试如下:
Apache反向代理请求到后端的Tomcat,node2编译安装httpd
[root@node2 ~]# rpm -e httpd #删除以前安装的httpd,自己编译安装
[root@node2 ~]# rpm -ql httpd
package httpd is not installed
[root@node2 tool]# tar -xf apr-1.5.2.tar.gz
[root@node2 tool]# cd apr-1.5.2
[root@node2 apr-1.5.2]# ./configure --prefix=/usr/local/apr
[root@node2 apr-1.5.2]# make &&make install
[root@node2 tool]# tar -xf apr-util-1.5.4.tar.gz
[root@node2 tool]# cd apr-util-1.5.4
[root@node2 apr-util-1.5.4]# ./configure --prefix=/usr/local/apr-util --with- apr=/usr/local/apr
[root@node2 apr-util-1.5.4]# make &&make install
[root@node2 tool]# tar -xf httpd-2.4.23.tar.gz
[root@node2 tool]# cd httpd-2.4.23
[root@node2 httpd-2.4.23]# ./configure --prefix=/usr/local/apache --sysconfdir=/etc/httpd --enable-so --enable-ssl --enable-cgi --enable-rewrite --with-zlib --with-pcre --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr-util --enable-mpms-shared=all --with-mpm=event --enable-proxy --enable-proxy-http --enable-proxy-ajp --enable-proxy-balancer --enable-lbmethod-heartbeat --enable-heartbeat --enable-slotmem-shm --enable-slotmem-plain --enable-watchdog
[root@node2 httpd-2.4.23]# make &&make install
为编译安装的Apache提供Sysv启动脚本
[root@node2 httpd-2.4.23]# vim /etc/rc.d/init.d/httpd
#!/bin/bash
#
# httpd Startup script for the Apache HTTP Server
#
# chkconfig: - 85 15
# description: Apache is a World Wide Web server. It is used to serve \
# HTML files and CGI.
# processname: httpd
# config: /etc/httpd/conf/httpd.conf
# config: /etc/sysconfig/httpd
# pidfile: /var/run/httpd.pid
# Source function library.
. /etc/rc.d/init.d/functions
if [ -f /etc/sysconfig/httpd ]; then
. /etc/sysconfig/httpd
fi
# Start httpd in the C locale by default.
HTTPD_LANG=${HTTPD_LANG-"C"}
# This will prevent initlog from swallowing up a pass-phrase prompt if
# mod_ssl needs a pass-phrase from the user.
INITLOG_ARGS=""
# Set HTTPD=/usr/sbin/httpd.worker in /etc/sysconfig/httpd to use a server
# with the thread-based "worker" MPM; BE WARNED that some modules may not
# work correctly with a thread-based MPM; notably PHP will refuse to start.
# Path to the apachectl script, server binary, and short-form for messages.
apachectl=/usr/local/apache/bin/apachectl
httpd=${HTTPD-/usr/local/apache/bin/httpd}
prog=httpd
pidfile=${PIDFILE-/var/run/httpd.pid}
lockfile=${LOCKFILE-/var/lock/subsys/httpd}
RETVAL=0
start() {
echo -n $"Starting $prog: "
LANG=$HTTPD_LANG daemon --pidfile=${pidfile} $httpd $OPTIONS
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${lockfile}
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
killproc -p ${pidfile} -d 10 $httpd
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
}
reload() {
echo -n $"Reloading $prog: "
if ! LANG=$HTTPD_LANG $httpd $OPTIONS -t >&/dev/null; then
RETVAL=$?
echo $"not reloading due to configuration syntax error"
failure $"not reloading $httpd due to configuration syntax error"
else
killproc -p ${pidfile} $httpd -HUP
RETVAL=$?
fi
echo
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status -p ${pidfile} $httpd
RETVAL=$?
;;
restart)
stop
start
;;
condrestart)
if [ -f ${pidfile} ] ; then
stop
start
fi
;;
reload)
reload
;;
graceful|help|configtest|fullstatus)
$apachectl $@
RETVAL=$?
;;
*)
echo $"Usage: $prog {start|stop|restart|condrestart|reload|status|fullstatus|graceful|help|configtest}"
exit 1
esac
exit $RETVAL
[root@node2 httpd-2.4.23]# chmod +x /etc/rc.d/init.d/httpd
[root@node2 httpd-2.4.23]# chkconfig --add httpd
[root@node2 httpd-2.4.23]# service httpd start
Starting httpd: [ OK ]
看来这个脚本的stop关闭httpd有问题
[root@node2 httpd-2.4.23]# service httpd stop
Stopping httpd: [FAILED]
[root@node2 httpd-2.4.23]# service httpd restart
Stopping httpd: [FAILED]
Starting httpd: httpd (pid 33077) already running
[ OK ]
[root@node2 httpd-2.4.23]# tail /usr/local/apache/logs/error_log
[Tue Jan 03 12:28:49.353372 2017] [mpm_event:notice] [pid 33077:tid 140099424745216] AH00489: Apache/2.4.23 (Unix) configured -- resuming normal operations
[Tue Jan 03 12:28:49.353815 2017] [core:notice] [pid 33077:tid 140099424745216] AH00094: Command line: '/usr/local/apache/bin/httpd'
通过httpd (pid 33077) already running的提示可知是httpd关闭时没有将pid文件删掉
[root@node2 httpd-2.4.23]# vim /usr/local/apache/logs/httpd.pid
33077
删除pid文件
[root@node2 httpd-2.4.23]# rm -rf /usr/local/apache/logs/httpd.pid
[root@node2 httpd-2.4.23]# vim /etc/httpd/httpd.conf
加入 PidFile "/var/run/httpd.pid"
[root@node2 httpd-2.4.23]# service httpd restart
Stopping httpd: [FAILED]
Starting httpd: [ OK ]
[root@node2 httpd-2.4.23]# service httpd restart
Stopping httpd: [ OK ]
Starting httpd: [ OK ]
OK,httpd的启动脚本正常,至此node4上的httpd编译安装完成
[root@node5 webapps]# vim /etc/httpd/conf/httpd.conf
#DocumentRoot "/var/www/html"
include /etc/httpd/extra/httpd-proxy.conf
启用以下模块
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
#负载均衡时依赖这两个内存共享模块
LoadModule proxy_module modules/mod_proxy.so
#让Apache反向代理的模块
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
#以http协议转发请求至后端Tomcat
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
#支持fast-cgi
LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
#支持scgi
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
#以ajp协议转发请求至后端Tomcat
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
#让Apache反向代理至后端Tomcat时支持负载均衡
LoadModule proxy_express_module modules/mod_proxy_express.so
[root@node2 ~]# /usr/local/apache/bin/httpd -D DUMP_MODULES |grep proxy
proxy_module (shared)
proxy_connect_module (shared)
proxy_ftp_module (shared)
proxy_http_module (shared)
proxy_fcgi_module (shared)
proxy_scgi_module (shared)
proxy_ajp_module (shared)
proxy_balancer_module (shared)
proxy_express_module (shared)
[root@node2 ~]# vim /etc/httpd/extra/httpd-proxy.conf
<VirtualHost *:80>
ProxyVia Off
ProxyRequests Off
ProxyPass / http://192.168.139.8:8080/
ProxyPassReverse / http://192.168.139.8:8080/
<Proxy *>
Require all granted
</Proxy>
<Location / >
Require all granted
</Location>
</VirtualHost>
注:
ProxyVia {On|Off|Full|Block}:用于控制在http首部是否使用Via首部(这样可以让client端知道请求时由哪个server代理的),主要用于在多级代理中控制代理请求的流向。默认为Off,即不启用此功能;On表示每个请求和响应报文均添加Via:;Full表示每个Via:行都会添加当前apache服务器的版本号信息;Block表示每个代理请求报文中的Via:都会被移除。
ProxyRequests {On|Off}:是否开启apache正向代理的功能;启用此项时为了代理http协议必须启用mod_proxy_http模块。同时,如果为apache设置了ProxyPass,则必须将ProxyRequests设置为Off,正反向代理不能同时使用
ProxyPreserveHost {On|Off}:如果启用此功能,则会让后端server支持虚拟主机;代理会将用户请求报文中的Host:行发送给后端的服务器,而不再使用ProxyPass指定的服务器地址。如果想在反向代理中支持虚拟主机,则需要开启此项,否则就无需打开此功能。
ProxyPass [path] !|url [key=value key=value ...]]:将后端服务器某URL与当前服务器的某虚拟路径关联起来作为提供服务的路径,path为当前服务器上的某虚拟路径,url为后端服务器上某URL路径。使用此指令时必须将ProxyRequests的值设置为Off。需要注意的是,如果path以“/”结尾,则对应的url也必须以“/”结尾,反之亦然。
如访问后端某应用程序的路径为 http://http192.168.139.8:8080/fprum
则前端Apache的代理为:ProxyPass /forum http://192.168.139.8/forum
即前端的uri必须和后端的uri保持一致(除非写大量的uri重写规则)
另外,mod_proxy模块在httpd 2.1的版本之后支持与后端服务器的连接池功能,连接在按需创建在可以保存至连接池中以备进一步使用。连接池大小或其它设定可以通过在ProxyPass中使用key=value的方式定义。常用的key如下所示:
◇ min:连接池的最小容量,此值与实际连接个数无关,仅表示连接池最小要初始化的空间大小。
◇ max:连接池的最大容量,每个MPM都有自己独立的容量;都值与MPM本身有关,如Prefork的总是为 1,而其它的则取决于ThreadsPerChild指令的值。
◇ loadfactor:用于负载均衡集群配置中,定义对应后端服务器的权重,取值范围为1-100。
◇ retry:当apache将请求发送至后端服务器得到错误响应时等待多长时间以后再重试。单位是秒钟。
如果Proxy指定是以balancer://开头,即用于负载均衡集群时,其还可以接受一些特殊的参数,如下所示:
◇lbmethod:apache实现负载均衡的调度方法,默认是byrequests(轮调),即基于权重将统计请求个数进行调度,bytraffic则执行基于权重的流量计数调度,bybusyness通过考量每个后端服务器的当前负载进行调度。
◇ maxattempts:放弃请求之前实现故障转移的次数,默认为1,其最大值不应该大于总的节点数。
◇ nofailover:取值为On或Off,设置为On时表示后端服务器故障时,用户的session将损坏;因此,在后端服务器不支持session复制时可将其设置为On。
◇ stickysession:调度器的sticky session的名字,根据web程序语言的不同,其值为JSESSIONID(用于Java的)或PHPSESSIONID(用于php的)。
上述指令除了能在banlancer://或ProxyPass中设定之外,也可使用ProxySet指令直接进行设置,如:
<Proxy balancer://hotcluster>
BalancerMember http://node1.zxl.com:8080 loadfactor=1
BalancerMember http://node2.zxl.com:8080 loadfactor=2
ProxySet lbmethod=bytraffic
</Proxy>
ProxyPassReverse:用于让apache调整HTTP重定向响应报文中的Location、Content-Location及URI标签所对应的URL,在反向代理环境中必须使用此指令避免响应报文和重定向报文绕过proxy服务器。
<Proxy *> Require all granted </Proxy>:允许所有用户访问
[root@node2 ~]# service httpd restart
这样就实现了让Apache依赖mod_proxy模块实现发现代理,接下来将演示让Apache依赖mod_jk模块实现反向代理
mod_jk是ASF的一个项目,是一个工作于apache端基于AJP协议与Tomcat通信的连接器,它是apache的一个模块,是AJP协议的客户端(服务端是Tomcat的AJP连接器)。
http://tomcat.apache.org/download-connectors.cgi
[root@node2 tool]# tar -xf tomcat-connectors-1.2.42-src.tar.gz
[root@node2 tool]# cd tomcat-connectors-1.2.42-src/native/
[root@node2 native]# ./configure --with-apxs=/usr/local/apache/bin/apxs
[root@node2 native]# make &&make install
[root@node2 native]# vim /etc/httpd/httpd.conf
#include /etc/httpd/extra/httpd-proxy.conf #注释掉刚才配置mod_proxy进行代理的配置文件
include /etc/httpd/extra/httpd-jk.conf #新加入用mod_jk模块进行代理的配置文件
[root@node2 native]# vim /etc/httpd/extra/httpd-jk.conf
LoadModule jk_module modules/mod_jk.so
启用jk_module模块
JkWorkersFile /etc/httpd/extra/workers.properties
jk主要使用一些worker进程和后端的tomcat通行,所以在这个文件中定义worker的一些属性
JkLogFile logs/mod_jk.log
JkLogLevel debug
以上两行与日志有关
JkMount /* TomcatB
所有发往/*下的请求都转发给TomcatB这个worker,此处的TomcatB是node5(192.168.139.9)上tomcat的核心配置文件server.xml中定义的JvmRoute(Jvm路由标签即Jvm实例的名称)
JkMount /status/ stat1
所有发往/status/路径的请求都转发给stat1这个worker,其中stat1为Apache的jk模块自带的一个实例,是让jk专门用来输出状态信息的一个自带的worker
[root@node2 apache]# vim /etc/httpd/extra/workers.properties
worker.list=TomcatB,stat1
两个worker的名称,分别为TomcatB、stat1
worker.TomcatB.port=8009
jk模块进行代理用的是ajp连接器(ajp协议),其后端Tomcat监听的端口为8009
注:后端8009,前端只有httpd的80端口,后端还有http的8080端口
worker.TomcatB.host=192.168.139.9
后端tomcat的ip
worker.TomcatB.type=ajp13
ajp13协议(ajp的1.3版本)
worker.TomcatB.lbfactor=1
定义的权重为1,主要在负载均衡后端server时定义
worker.stat1.type = status
此处根据需求可以定义的全部参数如下:
ajp13:此类型表示当前worker为一个运行着的Tomcat实例,其协议为ajp1.3。
lb:lb即load balancing,专用于负载均衡场景中的woker;此worker并不真正负责处理用户请求,而是将用户请求调度给其它类型为ajp13的worker。
status:用户显示分布式环境中各实际worker工作状态的特殊worker,它不处理任何请求,也不关联到任何实际工作的worker实例。具体示例如请参见后文中的配置。
worker其它常见的属性说明:
host:Tomcat 的worker实例所在的主机;
port:Tomcat 实例上AJP1.3连接器的端口;
connection_pool_minsize:最少要保存在连接池中的连接的个数;默认为pool_size/2;
connection_pool_timeout:连接池中连接的超时时长;
mount:由当前worker提供的context路径,如果有多个则使用空格格开;此属性可以由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,即根据请求的个数进行调度;T表示根据已经发送给worker的实际流量大小进行调度;B表示根据实际负载情况进行调度。
sticky_session:在将某请求调度至某worker后,源于此址的所有后续请求都将直接调度至此worker,实现将用户session与某worker绑定。默认为值为1,即启用此功能。如果后端的各worker之间支持session复制,则可以将此属性值设为0。
配置好后重启测试
[root@node2 apache]# service httpd restart
基于mod_proxy模块实现负载均衡
[root@node2 apache]# vim /etc/httpd/httpd.conf
include /etc/httpd/extra/httpd-proxy.conf
#include /etc/httpd/extra/httpd-jk.conf
[root@node2 apache]# vim /etc/httpd/extra/httpd-proxy.conf
ProxyRequests Off #关闭正向代理
<proxy balancer://lbcluster1>
BalancerMember ajp://192.168.139.8:8009 loadfactor=1 route=TomcatA
#注意为ajp协议代理
BalancerMember ajp://192.168.139.9:8009 loadfactor=1 route=TomcatB
ProxySet lbmethod=byrequests
#轮调的调度算法
</proxy>
<VirtualHost *:80>
ServerAdmin admin@zxl.com
ServerName node2.zxl.com
ProxyVia On
ProxyPass / balancer://lbcluster1/ #一定要注意前后uri一致
ProxyPassReverse / balancer://lbcluster1/
<proxy *>
Require all granted
</proxy>
<Location />
Require all granted
</Location>
</VirtualHost>
[root@node2 apache]# service httpd configtest
AH00526: Syntax error on line 5 of /etc/httpd/extra/httpd-proxy.conf:
ProxySet: unknown lbmethod lbmethod=byrequests; balancer://lbcluster1
[root@node2 apache]# vim /etc/httpd/httpd.conf
启用以下模块
LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
[root@node2 apache]# service httpd restart
Stopping httpd: [ OK ]
Starting httpd: [ OK ]
浏览器测试
刷新
基于mod_jk模块实现负载均衡
[root@node2 apache]# vim /etc/httpd/httpd.conf
#include /etc/httpd/extra/httpd-proxy.conf
include /etc/httpd/extra/httpd-jk.conf
[root@node2 apache]# vim /etc/httpd/extra/httpd-jk.conf
LoadModule jk_module modules/mod_jk.so
JkWorkersFile /etc/httpd/extra/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel debug
JkMount /* lbcluster1
JkMount /status/ stat1
[root@node2 apache]# vim /etc/httpd/extra/workers.properties
worker.list = lbcluster1,stat1
worker.TomcatA.type = ajp13
worker.TomcatA.host = 192.168.139.8
worker.TomcatA.port = 8009
worker.TomcatA.lbfactor = 5
worker.TomcatB.type = ajp13
worker.TomcatB.host = 192.168.139.9
worker.TomcatB.port = 8009
worker.TomcatB.lbfactor = 5
worker.lbcluster1.type = lb
worker.lbcluster1.sticky_session = 0
worker.lbcluster1.balance_workers = TomcatA, TomcatB
[root@node2 apache]# service httpd restart
浏览器测试
刷新
基于mod_jk模块实现session的会话绑定
[root@node2 apache]# vim /etc/httpd/extra/workers.properties
worker.lbcluster1.sticky_session = 1
[root@node2 apache]# service httpd restart
则不管如何刷新,请求始终定向到同一个后端tomcat,且session信息始终不变
换个浏览器不断刷新也一样
这样就实现了session信息的绑定
基于虚拟主机的反向代理,且实现状态信息的输出
[root@node2 ~]# vim /etc/httpd/httpd.conf
include /etc/httpd/extra/httpd-proxy.conf
#include /etc/httpd/extra/httpd-jk.conf
[root@node2 ~]# vim /etc/httpd/extra/httpd-proxy.conf
[root@node2 ~]# vim /etc/httpd/extra/httpd-proxy.conf
ProxyRequests Off
<proxy balancer://lbcluster1>
BalancerMember ajp://192.168.139.8:8009 loadfactor=1 route=TomcatA
BalancerMember ajp://192.168.139.9:8009 loadfactor=1 route=TomcatB
ProxySet lbmethod=byrequests
</proxy>
<VirtualHost *:80>
ServerName node2.zxl.com
<Location /balancer-manager>
SetHandler balancer-manager
Require all granted
</Location>
</VirtualHost>
<VirtualHost *:80>
ServerAdmin admin@zxl.com
ServerName node2.zxl.com
ProxyVia On
ProxyPass / balancer://lbcluster1/
ProxyPassReverse / balancer://lbcluster1/
<proxy *>
Require all granted
</proxy>
<Location />
Require all granted
</Location>
</VirtualHost>
[root@node2 ~]# service httpd restart
但是这样这前面定义一个balancer-manager虚拟机后就不能通过访问http://192.168.139.4来实现负载均衡,如下图
可以这样定义
[root@node2 ~]# vim /etc/httpd/extra/httpd-proxy.conf
ProxyRequests Off
<proxy balancer://lbcluster1>
BalancerMember ajp://192.168.139.8:8009 loadfactor=1 route=TomcatA
BalancerMember ajp://192.168.139.9:8009 loadfactor=1 route=TomcatB
ProxySet lbmethod=byrequests
</proxy>
<VirtualHost *:80>
ServerAdmin admin@zxl.com
ServerName node2.zxl.com
ProxyVia On
ProxyPass / balancer://lbcluster1/
ProxyPassReverse / balancer://lbcluster1/
<Location /balancer-manager>
Sethandler balancer-manager
Proxypass ! #表示对/balancer-manager请求不进行转发(状态信息是由代理server输出的,!表示 #不做代理)
Require all granted
</Location>
<proxy *>
Require all granted
</proxy>
<Location />
Require all granted
</Location>
</VirtualHost>
[root@node2 ~]# service httpd restart
这样既能通过http://192.168.139.4/balancer-manager/输出状态信息
也能通过访问htp://192.168.139.4/实现负载均衡
Tomcat的内部有一个叫做cluster的组件,在这个组件内部有许多的会话管理器
1.标准会话管理器(StandardManager):
<Manager className="org.apache.catalina.session.StandardManager"
maxInactiveInterval="7200"/>
注:org.apache为反写的域名、catalina为项目名、session为项目对应的功能名、standmanager为具体的实现这名称(会话管理器名称)、maxInactiveInterval="7200"则表示持久连接时间为7200s,即一个用户两个小时后再来访问才会被识别为新用户
默认保存于$CATALINA_HOME/work/Catalina/<hostname>/<webapp-name>/下的SESSIONS.ser文件中。
maxActiveSessions:最多允许的活动会话数量,默认为-1,表示不限制;
maxInactiveInterval:非活动的会话超时时长,默认为60s;
pathname:会话文件的保存目录;
使用标准会话管理器后Tomcat就算关闭再重启session信息也不会丢失,因为标准会话管理器会没个一段时间将内存中的session同步到磁盘文件上进行保存;但有个弊端就是不能实现和其他节点进行session共享,一旦当前节点挂了,则用户的session信息也就丢了
2.持久会话管理器(PersistentManager):
将会话数据保存至持久存储中,并且能在服务器意外中止后重新启动时重新加载这些会话信息。持久会话管理器支持将会话保存至文件存储(FileStore)或JDBC存储(基于jdbc进行连接的关系型数据库中)。因为是直接将用户信息保存在磁盘上,所以不会出现标准会话管理器如果server突然挂了,造成session没来的即从内存写入磁盘这种情况,但同样无法实现session在隔节点共享
保存至文件中的示例:
<Manager className="org.apache.catalina.session.PersistentManager"
saveOnRestart="true">
<Store className="org.apache.catalina.session.FileStore"
directory="/data/tomcat-sessions"/>
</Manager>
每个用户的会话会被保存至directory指定的目录中的文件中,文件名为<session id>.session,并通过后台线程每隔一段时间(checkInterval参数定义,默认为60秒)检查一次超时会话。
保存至JDBCStore中的示例:
<Manager className="org.apache.catalina.session.PersistentManager"
saveOnRestart="true">
<Store className="org.apache.catalina.session.JDBCStore"
driverName="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mydb?user=jb;password=pw"/>
</Manager>
3.Deltamanager会话管理器,它可以将各个节点建成类似HA的集群。各节点之间可以传递心跳信息,且任何用户通过前端代理连接到后端节点后,其会话信息会被同步复制到每一个节点,这样即使当前节点挂了,用户请求下次被代理到其他接点,其session信息任然存在;但这种为每一个节点都进行会话信息的同步会占用大量的带宽,容易消耗过多流量
4.BackUp会话管理器,与DeltaManager不同的是其session信息只同步到相应的备份节点上(类似于HA中的故障转移域),而不会为每个节点都同步,且不同的节点可以相互进行备份,这样既实现了session共享也避免了占用过多的网络流量
下面将演示基于DeltaManager会话管理器实现各节点的session共享集群
注:所有启用集群功能的web应用程序,其web.xml中都须添加<distributable/>才能实现集群功能。如果某web应用程序没有自己的web.xml,也可以通过复制默认的web.xml至其WEB-INF目录中实现。
[root@node4 webapps]# mkdir WEB-INF
[root@node4 webapps]# cp /usr/local/tomcat/conf/web.xml WEB-INF/
[root@node4 webapps]# vim WEB-INF/
加入
<distributable/>
[root@node4 webapps]#scp WEB-INF/ node5:/web/webapps/
定义cluster(集群)及定义如何发送自己的心跳信息、如何接受别的节点的心跳信息、监听的端口、地址、通过那个组播进行信息传输、等待对放的超时时间等等;说白了就是tomcat需要你自己定义了底层的通信信道即如何进行各节点的信息、心跳等传输(而以前用的corosync、heartbeat等不用自己定义怎么进行信息传输)
[root@node4 ~]# vim /usr/local/tomcat/conf/server.xml
在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.50.10.1" bind="192.168.139.8" port="45564"
frequency="500" dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="192.168.139.8" port="4000" autoBind="100"
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.apaalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
filter=".*\.gif;.*\.js;.*\.jpg;.*\.htm;.*\.html;.*\.txt;"/>
<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>
[root@node4 ~]# catalina.sh stop
[root@node4 ~]# catalina.sh configtest
[root@node4 ~]# catalina.sh start
[root@node5 ~]# vim /usr/local/tomcat/conf/server.xml
同样加入上面的内容,但要将ip改为192.168.139.9
[root@node5 ~]# catalina.sh stop
[root@node5 ~]# catalina.sh configtest
[root@node5 ~]# catalina.sh start
刷新后可以看到重新代理到TomcatB节点但session没有改变
这样就实现了负载均衡和session共享(基于DeltaManager会话管理器)
注:虽然说BackManager会话管理器设计的更好,但由于Tomcat集群一般节点不会很多,所以session信息的传递对带宽的影响还是比较小的
其实也可以用Nginx的upstream定义一个集群实现反向代理Tomcat,但Nginx反向代理只能基于http协议进行通信和请求的转发;而Apache可以实现基于http/ajp进行通信,且在使用ajp协议时,可以关闭后端Tomcat的http连接器,让其只能以ajp协议收到前端Apache转发用户的JSP请求,这样就可以保证后端Tomcat服务器的安全
下面是Nginx反向代理Tomcat集群的演示
[root@node2 ~]# service httpd stop
Stopping httpd: [ OK ]
[root@node2 nginx]# vim /etc/nginx/nginx.conf
upstream Tomcat_Cluster {
server 192.168.139.8:8080;
server 192.168.139.9:8080;
}
location / {
root html;
index index.html index.htm;
proxy_pass http://Tomcat_Cluster;
}
[root@node2 ~]# service nginx start
刷新
本次实验结束!