Tomcat负载均衡集群--Nginx

调度器:172.18.0.69

Tomcat后端服务器1:172.18.0.67

Tomcat后端服务器2:172.18.0.68

(1)在tomcat主机上创建测试页

mkidr  -pv  /usr/share/tomcat/webapps/myapp/{classes,lib,WEB-INF}

vim /usr/share/tomcat/webapps/myapp/index.jsp 

<%@ page language="java" %>

<html>
<head><title>TomcatA</title></head>
<body>
<h1><font color="red">TomcatA.magedu.com</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("magedu.com","magedu.com"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>

mkidr  -pv  /usr/local/tomcat/webapps/myapp/{classes,lib,WEB-INF}

vim /usr/local/tomcat/webapps/test/index.jsp 

<%@ page language="java" %>

<html>
<head><title>TomcatB</title></head>
<body>
<h1><font color="red">TomcatB.magedu.com</font></h1>
<table align="centre" border="1">
<tr>
<td>Session ID</td>
<% session.setAttribute("magedu.com","magedu.com"); %>
<td><%= session.getId() %></td>
</tr>
<tr>
<td>Created on</td>
<td><%= session.getCreationTime() %></td>
</tr>
</table>
</body>
</html>

(2)配置Nginx

  vim  /etc/nginx/nginx.conf打开Nginx配置文件,

    upstream tcsrvs{

server  172.18.0.67:8080

server  172.18.0.68:8080

}

定义负载均衡服务器组

              location / {

proxy_pass http://tcsrvs;
}

实验结果:请求将被轮流调度至两台后端主机

Tomcat负载均衡集群--httpd(基于http协议)

调度器:172.18.0.69

Tomcat后端服务器1:172.18.0.67

Tomcat后端服务器2:172.18.0.68

(1)在tomcat主机上创建测试页

参考上一个实验

(2)配置httpd

 vim  /etc/httpd/conf.d/http-tomcat.conf

<proxy balancer://tcsrvs>

BalancerMember http://172.18.0.67:8080

BalancerMember http://172.18.0.68:8080

ProxySet lbmethod=byrequests
</Proxy>
<VirtualHost *:80>
ServerName lb.magedu.com
ProxyVia On
ProxyRequests Off
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
ProxyPass / balancer://tcsrvs/
ProxyPa***everse / balancer://tcsrvs/
<Location />
Require all granted
</Location>
</VirtualHost>

其中,

       ProxyRequests Off关闭正向代理

      ProxyVia On

作为代理服务器,能够操纵两级报文:客户端发送给后端服务器的请求报文(客户端->代理服务器->后端服务器,当客户端请求报文到达代理服务器时,代理服务器改动后再发给后端服务器)、后端服务器发送给客户端的响应报文(后端服务器->代理服务器->客户端,当后端服务器响应报文到达代理服务器时,代理服务器改动后再发给客户端)。默认情况下代理服务器是把客户端发来的请求报文原封不动的直接发送给后端服务器,但可以在原封不动的基础上增、删、改。当后端服务器响应报文到达代理服务器时,代理服务器可以加上ProxyVia首部,告诉客户端通过哪台主机来获取到相关资源,即指明代理服务器,然后再发给客户端。

      <Proxy *>

      Require all granted

      </Proxy>

     对代理模块做访问控制

      ProxyPreserveHost On

       当用户在客户端浏览器输入http://www.a.com时,客户端浏览器封装请求报文时会在请求报文首部封装host=www.a.com,即所请求的目标主机的主

机头。因为浏览器的地址经dns解析后是IP地址,一般是代理服务器即调度器的ip地址,若调度器上有多个虚拟主机,则请求到达虚拟主机时,调度器

就无法知道客户端请求的是哪个虚拟主机,在需要将请求代理到后端时,若后端服务器上与调度器的虚拟主机一一映射,则可以加上

ProxyPreserveHost,即保留客户端请求报文中的主机头,由后端服务器上相应的虚拟主机响应,但一般情况下后端服务器上与调度器的虚拟主机并不

是一一映射。

     <proxy balancer://tcsrvs>

      BalancerMember http://172.18.0.67:8080

      BalancerMember http://172.18.0.68:8080

     ProxySet lbmethod=byrequests

    </Proxy>

      定义负载均衡服务器组

     lbmethod定义调度算法:

     byrequests相当于roundrobin,即轮询,若有权重,则相当于加权轮询wrr

     bybusiness相当于lc算法,根据后端服务器的繁忙程度,将请求调度到相对空闲的主机

     bytraffic根据网络流量

      ProxyPass / balancer://tcsrvs/

     将请求调度到后端的负载均衡服务器组,最后的/必须加,表示替换

     实验结果:请求将被轮流调度至两台后端主机

Tomcat负载均衡集群--httpd(基于ajp协议)

调度器:172.18.0.69

Tomcat后端服务器1:172.18.0.67

Tomcat后端服务器2:172.18.0.68

(1)在tomcat主机上创建测试页

参考上一个实验

(2)配置httpd

 vim  /etc/httpd/conf.d/ajp-tomcat.conf

<proxy balancer://tcsrvs>

BalancerMember ajp://172.18.0.67:8009

BalancerMember ajp://172.18.0.68:8009
ProxySet lbmethod=byrequests
</Proxy>
<VirtualHost *:80>
ServerName lb.magedu.com
ProxyVia On
ProxyRequests Off
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
ProxyPass / balancer://tcsrvs/
ProxyPa***everse / balancer://tcsrvs/
<Location />
Require all granted
</Location>
</VirtualHost>

即只修改端口和协议即可

实验结果:请求将被轮流调度至两台后端主机

会话保持

        实现会话保持的三种方式:session sticky、session cluster、session server

session sticky

       有三种逻辑:

      (1)静态映射:将后端服务器根据权重映射为多台虚拟主机,如A、B、C三台主机的权重分别为3、2、1,则将A、B、C三台主机分别映射为3、

2、1台虚拟主机,对应于一个长度为6、下标从0到5的数组。当用户请求到达时,假设采用的调度算法是基于源地址进行调度,则对客户端的源地址做

哈希运算,将得到的哈希值对6取余,余数一定在0-5之间,就可将调度至数组下标与余数相等的后端主机。当该客户端再次访问时,因为对地址做哈希

运算的结果是一样的,对6取余的结果也是一样的,就可以将该客户端调度至同一台后端主机(基于uri或基于cookie调度则对uri或cookie做哈希)。存

在的问题:当后端主机发生变动时,权重也会发生变动,之前基于权重所作的映射都将失效,影响全局。

       (2)一致性哈希:同样将后端服务器根据权重映射为多台虚拟主机,如A、B、C三台主机的权重分别为3、2、1,映射时将主机A的ip加三个随机

数得到三个ip,B、C根据权重做同样计算,然后将最终得到的6个ip对2的32次方取余,余数在0-(2^32-1)之间,从而将其映射到哈希环上。当用户

请求到达时,假设采用的调度算法是基于源地址进行调度,则对客户端的源地址做哈希运算,将得到的哈希值对2的32次方取余,余数一定在0-(2^32-

1)之间,也将其映射到哈希环上,则客户端请求与后端服务器虚拟映射节点相间分布,就可将该请求顺时针或逆时针调度至与其相邻的后端服务器。

当该客户端再次访问时,因为对地址做哈希运算的结果是一样的,对2的32次方取余的结果也是一样的,就可以将该客户端调度至同一台后端主机(基

于uri或基于cookie调度则对uri或cookie做哈希)。由于后端服务器的数量较少,可能导致大部分后端主机密集分布在哈希环某区域,此时可将后端主

机按权重比扩大,如A、B、C三台主机的权重分别为3、2、1,映射时分别映射为300、200、100台主机,从而避免哈希环的偏斜。一致性哈希对某一

固定数值取余,当后端服务器变动时,只会影响相邻节点的调度,不会影响全局。

      (3)为了追踪用户身份,当客户端访问服务器时,服务器会在返回的响应报文中添加cookie信息(包含客户端的身份标识),当该客户端再次访

问时,会在请求报文中携带该cookie,以便调度器识别用户身份,获取其会话信息。要实现会话绑定,即将来自同一客户端的相同请求始终调度至同一

后端服务器,可以在此基础上添加为客户端提供服务的后端服务器标识;调度器在后端服务器发送给客户端的cookie中添加一个参数,该参数表示出了

本次请求被调度到哪台后端服务器,当客户端携带该cookie再次访问时,调度器通过该参数就可以获得上次调度信息,然后再将该客户端请求调度至该

后端服务器,从而实现会话绑定。

session cluster

       当某客户端第一次请求某资源时,调度器根据调度算法将其调度至某台后端服务器,该后端服务器将本次会话信息通知给同一多播集群中的其他服

务器,即属于同一多播集群的主机都保存有该会话信息,当该客户端再次请求该资源时,调度器仍然只需要考虑调度算法本身,根据调度算法将请求调

度至某台后端服务器,无论调度至哪台后端主机,都有该会话信息,也就实现了会话绑定。

session server

       当某客户端第一次请求某资源时,调度器根据调度算法将其调度至某台后端服务器,该后端服务器将本次会话信息保存到memcache中,当该客户

端再次请求该资源时,调度器仍然只需要考虑调度算法本身,根据调度算法将请求调度至某台后端服务器,该后端服务器到后端的memcache获取与该

客户端相关的会话信息,也就实现了会话绑定。

session sticky--cookie实现

调度器:172.18.0.69

Tomcat后端服务器1:172.18.0.67

Tomcat后端服务器2:172.18.0.68

(1)在tomcat主机上创建测试页

参考第一个实验

(2)配置调度器

vim    vim  /etc/httpd/conf.d/ajp-tomcat.conf

Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED

<proxy balancer://tcsrvs>
BalancerMember http://172.18.100.67:8080 route=TomcatA loadfactor=1
BalancerMember http://172.18.100.68:8080 route=TomcatB loadfactor=1
ProxySet lbmethod=byrequests
ProxySet stickysession=ROUTEID
</Proxy>
<VirtualHost *:80>
ServerName lb.magedu.com
ProxyVia On
ProxyRequests Off
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
ProxyPass / balancer://tcsrvs/
ProxyPa***everse / balancer://tcsrvs/
<Location />
Require all granted
</Location>
</VirtualHost>

       其中,

      ProxySet stickysession=ROUTEID 表示代理时或调度时启用会话粘性

route=TomcatA 定义后端服务器标识

     Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
      在cookie的Set-Cookie首部添加ROUTEID参数
      调度器将某一用户请求调度至后台某个主机时,会将该后端服务器的标识赋给ROUTEID,然后将该参数插入后端服务器发送给客户端的cookie中,当客户端携带该cookie再次访问时,调度器通过该参数就可以获得上次调度信息,然后再将该客户端请求调度至该后端服务器,从而实现会话绑定。

来自于同一客户端的相同请求属于同一会话,因为只有第一次响应该客户端请求的后端服务器有该会话信息,所以若实现了会话绑定,实验结果应该是:无论如何刷新都调度至同一台后端服务器,且sessionid一定保持不变

session cluster

调度器:172.18.0.69

Tomcat后端服务器1:172.18.0.67

Tomcat后端服务器2:172.18.0.68

(1)在tomcat主机上创建测试页

参考第一个实验

(2)配置调度器

 vim  /etc/httpd/conf.d/ajp-tomcat.conf

<proxy balancer://tcsrvs>

BalancerMember http://172.18.100.67:8080 loadfactor=1
BalancerMember http://172.18.100.68:8080 loadfactor=2
ProxySet lbmethod=byrequests
</Proxy>
<VirtualHost *:80>
ServerName lb.magedu.com
ProxyVia On
ProxyRequests Off
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
ProxyPass / balancer://tcsrvs/
ProxyPa***everse / balancer://tcsrvs/
<Location />
Require all granted
</Location>
</VirtualHost>

调度器上只需要调度后端的

(3) 配置启用会话集群

 将下列配置放置于两个tomcat主机的<host>中;

vim  /usr/share/tomcat/conf/server.xml

<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="172.18.0.68"
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.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>

确保Engine的jvmRoute属性配置正确。

(4) 配置webapps
vim /usr/share/tomcat/webapps/myapp/WEB-INF/web.xml,添加<distributable/>元素;

来自于同一客户端的相同请求属于同一会话,因为每台后端服务器都有该会话信息,所以若实现了会话绑定,实验结果应该是:可能会被调度至不同后端服务器,但sessionid一定保持不变

session server

调度器:172.18.0.69

Tomcat后端服务器1:172.18.0.67

Tomcat后端服务器2:172.18.0.68

memcache服务器1:172.18.0.70

memcache服务器2:172.18.0.71

(1)装入类库

会话管理器:memcached-session-manager-${version}.jar 、memcached-session-manager-tc${6,7,8}-${version}.jar

Tomcat与memcache通信工具:spymemcached-${version}.jar

流式化工具:javolution-${version}.jar、msm-javolution-serializer-${version}.jar

上述几类工具都是类库,需要将其装入到Tomcat,即放入Tomcat的类库目录/usr/share/java/tomcat/

(2)配置调度器

   vim  /etc/httpd/conf.d/ajp-tomcat.conf

<proxy balancer://tcsrvs>

BalancerMember http://172.18.100.67:8080 loadfactor=1

BalancerMember http://172.18.100.68:8080 loadfactor=1
ProxySet lbmethod=byrequests
</Proxy>
<VirtualHost *:80>
ServerName lb.magedu.com
ProxyVia On
ProxyRequests Off
ProxyPreserveHost On
<Proxy *>
Require all granted
</Proxy>
ProxyPass / balancer://tcsrvs/
ProxyPa***everse / balancer://tcsrvs/
<Location />
Require all granted
</Location>
</VirtualHost>

调度器上只需要调度后端的

(3)在memcache服务器上只需安装memcache软件包,不需做其他配置

(4)将下列配置放置于两个tomcat主机的<host>中;

vim  /usr/share/tomcat/conf/server.xml

           <Context path="/test" docBase="/usr/local/tomcat/webapps/test" reloadable="true">

              <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
                memcachedNodes="n1:172.18.0.70:11211,n2:172.18.0.71:11211"
                failoverNodes="n1"
                requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
                transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"
              />
             </Context>

会话管理器可以定义在engine、host、context上,若定义在engine上,则表示对这个engine内部的所有host主机,对每个host主机上的所有应用都有效;若定义在host上,则表示对这个host主机上的所有应用都有效;若定义在context上,则表示对这个context定义的应用有效

      <Context path="/test" docBase="/usr/local/tomcat/webapps/test" reloadable="true">
      会话管理器定义在context上表示对这个context定义的myapp应用有效,即客户端访问myapp这个应用时,将会话信息保存在memcache中。
       memcachedNodes="n1:172.16.100.9:11211,n2:172.16.100.10:11211" failoverNodes="n1"
        memcachedNodes定义memcache节点,id:ip:端口,为了解决memcache的单点问题,设置两个memcache节点,保存会话时采用双写机制,即将会话信息同时写入两个memcache节点。failoverNodes定义即n1为故障转移节点,即备用节点,将会话信息同时写入两个memcache节点,读取会话信息时只从主节点n2读取,当主节点故障时,才从备用节点读取会话信息
      requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"当访问myapp应用下的jpg、css等静态资源时,不保存会话信息到memcache。
      transcoderFactoryClass定义调用的流式化工具

来自于同一客户端的相同请求属于同一会话,因为会话信息保存在memcached中,当请求被调度至某台后端服务器时,该服务器到memcached上去获取会话信息,所以若实现了会话绑定,实验结果应该是:可能会被调度至不同后端服务器,但sessionid一定保持不变。