Apache Httpd负载均衡Tomcat并实现Session Sticky和Session Cluster


上篇文章我们介绍如何构建一个 LANMT平台并搭建一个 jspxcms, 这次我们介绍使用 apache httpd负载均衡 Tomcat并实现 Session StickySession Cluster


实验步骤

安装配置tomcat


我们首先配置 Tomcat, 安装过程这里不做叙述,


安装 Tomcatnode2,3 都要操作
[root@node1 ~]# [root@node1 ~]# wget http://mirrors.cnnic.cn/apache/tomcat/tomcat-8/v8.0.33/bin/apache-tomcat-8.0.33.tar.gz
[root@node1 ~]# tar xf apache-tomcat-8.0.33.tar.gz  -C /usr/local/
[root@node1 ~]# cd /usr/local ; ln -sv apache-tomcat-8.0.33 tomcat ; cd ~
[root@node1 ~]# wget http://download.oracle.com/otn-pub/java/jdk/8u77-b03/jdk-8u77-linux-x64.rpm #如果下载出错, 使用浏览器下载
[root@node1 ~]# rpm -ivh jdk-8u77-linux-x64.rpm   #安装JDK

[root@node1 ~]# vim /etc/profile.d/java.sh     #定义JAVA_HOME
export JAVA_HOME=/usr/java/latest
export PATH=$JAVA_HOME/bin:$PATH

[root@node1 ~]# vim /etc/profile.d/tomcat.sh    #配置tomcat的环境变量
export CATALINA_BASE=/usr/local/tomcat
export PATH=$CATALINA_BASE/bin:$PATH

[root@node1 ~]# source /etc/profile.d/tomcat.sh
[root@node1 ~]# source /etc/profile.d/java.sh
配置虚拟主机
[root@node2 ~]# vim /usr/local/tomcat/conf/server.xml   

#设置默认虚拟主机为web1.anyisalin.com #node3的jvmRoute设置为TomcatB
<Engine name="Catalina" defaultHost="web1.anyisalin.com" jvmRoute="TomcatA">  


#添加虚拟主机
<Host name="web1.anyisalin.com" appBase="/data/webapps/" unpackWARS="true" autoDeploy="true">
   <Context path="" docBase="/data/webapps" reloadable="true" />
     <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/data/logs"
        prefix="web1_access_log." suffix=".txt"
        pattern="%h %l %u %t "%r" %s %b" />
</Host>

[root@node2 ~]# mkdir -PV /data/webapps/{WEB-INF,META-INF,classes,lib}  #创建目录
node2 创建网页文件
[root@node2 ~]# cd /data/webapps/

[root@node2 ~]# vim 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>

[root@node2 webapps]# catalina.sh start #启动tomcat

[root@node2 ~]# cd /data/webapps/

[root@node2 ~]# vim 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>

[root@node2 webapps]# catalina.sh start #启动tomcat
node3 创建网页文件
[root@node3 ~]# cd /data/webapps/

[root@node3 ~]# vim index.jsp  #创建网页文件
<%@ page language="java" %>
<html>
  <head><title>TomcatB</title></head>
  <body>
    <h1><font color="blue">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>

[root@node3 webapps]# catalina.sh start #启动tomcat

[root@node3 ~]# cd /data/webapps/

[root@node3 ~]# vim index.jsp  #创建网页文件
<%@ page language="java" %>
<html>
  <head><title>TomcatB</title></head>
  <body>
    <h1><font color="blue">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>

[root@node3 webapps]# catalina.sh start #启动tomcat
访问测试

安装配置Apache Httpd并测试

安装 httpdnode1
[root@node1 ~]# yum install httpd -y

[root@node1 ~]# yum install httpd -y


配置 httpd


mod_Proxy_balancer


[root@node1 ~]# cd /etc/httpd/conf.d/

[root@node1 conf.d]# vim virt.conf 
<Proxy balancer://lbcluster>
   BalancerMember ajp://172.16.1.3:8009 loadfactor=1 route=TomcatA
   BalancerMember ajp://172.16.1.4:8009 loadfactor=1 route=TomcatB
</Proxy>

<VirtualHost *:80>
  ServerName www.anyisalin.com
  ProxyVia On
  ProxyRequests Off
  ProxyPassReverse / balancer://lbcluster/
  ProxyPass / balancer://lbcluster/
</VirtualHost>

[root@node1 conf.d]# service httpd start  #启动httpd
测试负载均衡效果

实现session sticky


如果 tomcat运行着一个动态站点, 那么上面这种 byrequest的负载均衡调度算法就有很大问题了, 可能刚登录站点再刷新又回到没有登录的状态了, 所以我们就要实现 session sticky, 其实 session sticky就是我们之前 lvs的sh算法, 和 nginxip_hash实现的效果


编辑 httpd 配置文件
[root@node1 ~]# vim /etc/httpd/conf.d/virt.conf   
## 主要添加两段参数
##Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
##ProxySet stickysession=ROUTEID 

Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED #新添加的
<Proxy balancer://lbcluster>
   BalancerMember ajp://172.16.1.3:8009 loadfactor=1 route=TomcatA
   BalancerMember ajp://172.16.1.4:8009 loadfactor=1 route=TomcatB
   ProxySet stickysession=ROUTEID  #新添加的
</Proxy>

<VirtualHost *:80>
  ServerName www.anyisalin.com
  ProxyVia On
  ProxyRequests Off
  ProxyPassReverse / balancer://lbcluster/
  ProxyPass / balancer://lbcluster/
</VirtualHost>

[root@node1 conf.d]# service httpd restart   #重启httpd


测试 session sticky 效果

如下图, 我们成功的将session绑定在了 TomcatA(Node2) 主机上


实现Session Cluster


我们使用 session sticky会有很多问题, 如果某台主机宕机了, 那么用户的session就丢失了, 所以我们还可以通过 session cluster保存用户会话


什么是 Session Cluster


正常情况下 session信息是保存在用户所访问的服务器上,服务器宕机, 用户的 session就丢失了, 但是我们可以通过 session cluster的方式来实现将用户的 session信息保存在后端所有的服务器上, 这样来, 无论用户在访问哪一台服务器, session都不会丢失


四种常见的 session manager


  • StandardManager
  • PersistentManager: 可以将 session信息保存在持久存储中
  • DeltaManager: 将 session信息通过多播的形式共享到其他节点
  • BackupManager: 将 session信息共享到特定的一个节点上


注意: 我们要删掉刚才对 session sticky 的配置
编辑 tomcat 作配置文件
[root@node2 conf]# vim server.xml
下列内容放在<Host />内

<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.1.7"
         port="45564"
         frequency="500"
         dropTime="3000"/>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
          address="auto"
          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.ClusterSessionListener"/>
</Cluster>
配置 WEB-INF 中的 web.xml 文件
[root@node2 conf]# cd /data/webapps/
[root@node2 webapps]# cp /usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml WEB-INF/  #拷贝一份模板
[root@node2 webapps]# vim WEB-INF/web.xml
#添加下面这一行,在</web-app>内
<distributable/>

##注意: 这里是node3
[root@node3 webapps]# scp node2.anyisalin.com:/usr/local/tomcat/conf/server.xml /usr/local/tomcat/conf/server.xml  #复制node2配置文件
[root@node3 webapps]# scp node2.anyisalin.com:/data/webapps/WEB-INF/web.xml /data/webapps/WEB-INF/ 

[root@node2 webapps]# catalina.sh stop  #重启tomcat
[root@node2 webapps]# catalina.sh start 

[root@node3  webapps]# catalina.sh stop  #重启tomcat
[root@node3 webapps]# catalina.sh start 

[root@node2 conf]# cd /data/webapps/
[root@node2 webapps]# cp /usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml WEB-INF/  #拷贝一份模板
[root@node2 webapps]# vim WEB-INF/web.xml
#添加下面这一行,在</web-app>内
<distributable/>

##注意: 这里是node3
[root@node3 webapps]# scp node2.anyisalin.com:/usr/local/tomcat/conf/server.xml /usr/local/tomcat/conf/server.xml  #复制node2配置文件
[root@node3 webapps]# scp node2.anyisalin.com:/data/webapps/WEB-INF/web.xml /data/webapps/WEB-INF/ 

[root@node2 webapps]# catalina.sh stop  #重启tomcat
[root@node2 webapps]# catalina.sh start 

[root@node3  webapps]# catalina.sh stop  #重启tomcat
[root@node3 webapps]# catalina.sh start


session cluster 测试

如下图, 现在我们即使使用 byrequests 算法也不会丢失 sesssion


我们本文实现了 tomcat的负载均衡和 session保持的各种方法, 但是还是有很大问题, 我们试想在用户并发高的场景下, 多台 tomcat服务器一直在多播, 很可能造成网络拥塞, 所以我们还有一种解决方案 session server, 使用单台服务器保存用户的 session信息, 我们会在下篇文章向大家介绍!