[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.

wKiom1hrIzLhL-TMAAGG297c5So568.png



[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


wKioL1hrOKWR7QJYAABUE6uB52k243.png


[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测试如下:

wKioL1hrQLbzLCI-AAAtR9NzZYw901.png




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/

  ProxyPa***everse / 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>


ProxyPa***everse:用于让apache调整HTTP重定向响应报文中的Location、Content-Location及URI标签所对应的URL,在反向代理环境中必须使用此指令避免响应报文和重定向报文绕过proxy服务器。


<Proxy *> Require all granted </Proxy>:允许所有用户访问

   

 



[root@node2 ~]# service httpd restart


wKioL1hrZprBmJYVAAA9nrV4qFk598.png


这样就实现了让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


wKioL1hrcePRgmlHAAAp03RJ-K8120.png


基于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一致

ProxyPa***everse / 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  ]


浏览器测试

wKioL1hrtvnSQ0TCAAAtbG_vsHY888.png

刷新

wKiom1hrtw6DYXiIAAAs5WSVfTs930.png

基于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


浏览器测试

wKiom1hrwhCiITilAAAt1hf9gM4869.png

刷新

wKiom1hrwiXwVo00AAArCA_BPJ4601.png



基于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信息始终不变

wKioL1hrwtmAQzHxAAAo8tqJLYA768.png

换个浏览器不断刷新也一样

wKiom1hrwxKz6NenAAAtnAkmmWA098.png

这样就实现了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/

 ProxyPa***everse / balancer://lbcluster1/

 <proxy *>

  Require all granted

 </proxy>

 <Location />

  Require all granted

 </Location>

</VirtualHost>

[root@node2 ~]# service httpd restart

wKiom1hsh4eRpdQbAACyvQsRIsI229.png


但是这样这前面定义一个balancer-manager虚拟机后就不能通过访问http://192.168.139.4来实现负载均衡,如下图


wKioL1hsiDiw87cAAAAtzJEn1Z8880.png


可以这样定义

[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/

 ProxyPa***everse / 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/输出状态信息

wKiom1hsiTfiRoUcAACfxARwGeU763.png

也能通过访问htp://192.168.139.4/实现负载均衡

wKioL1hsiTjDgFh7AAAog_b56Dc812.png

wKiom1hsiTjyKr4zAAAp5Q8Hu2E984.png




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

 

wKiom1hsrXbTCTAtAACWxxXzZgM859.png

刷新后可以看到重新代理到TomcatB节点但session没有改变

wKiom1hsrXfzYLmeAACThgCumsA349.png


这样就实现了负载均衡和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


wKioL1hst6LyZAGAAAAssnlKAaM079.png

刷新

wKiom1hst6OTWYXuAAAqedpBuzY440.png


本次实验结束!