一、概述

之前文章已经描述了企业高可用负载相关的架构及实现,其中常用的nginx或haproxy,LVS结合keepalived做前端高可用调度器;但之前没有提到会话高可用保持;
本文通过 Tomcat Session Replication Cluster(tomcat自带)和tomcat结合memcat及第三方组件实现Tomcat Memcache Session Server高可用会话缓存服务;
实现的效果:
同一客户端访问业务网站,经过调度器负载调度到达后端,不管选择的是那个后端,session ID都不变,都保存在两台或多台的memcached缓存中(负载冗余);以保持持会话;

架构图:
Nginx+Tomcat+memcached高可用会话保持
说明:客户端请求时nginx通过负载调度算法将请求调度至某一后端服务器;tomcat 把会话通过组播的方式复制到集群各节点;所有节点共享会话;

Nginx+Tomcat+memcached高可用会话保持
说明:客户端请求时nginx通过负载调度算法将请求调度至某一后端服务器;并把session存储到两台memcached中;客户端刷新(不换浏览器)时,请求换成另一个后端服务器响应时session ID保持不变;

测试环境:
nginx: CentOS7 epel 安装nginx WAN:172.16.3.152 LAN:192.168.10.254
tomcat A: CentOS7 node1.san.com epel 安装 tomcat 7 openjdk-1.8.0 memcached(现实环境中单独服务器)
tomcat B: CentOS7 nodde2.san.com epel 安装 tomcat 7 openjdk-1.8.0 memcached 现实环境中单独服务器)
测试客户端ubuntu 16.04

cat /etc/hosts
172.16.3.152       www.san.com

二、安装配置集群

nginx安装

[root@nginx ~]# yum install epel-release -y
[root@nginx ~]# yum install nginx -y

nginx配置
在/etc/nginx/nginx.conf http段添加如下行

    upstream tcsrvs {
        server 192.168.10.11:8080;
        server 192.168.10.12:8080;
        }

cat /etc/nginx/conf.d/san.com.conf

[root@nginx ~]# cat /etc/nginx/conf.d/san.com.conf 
server {
            listen 80;
            server_name www.san.com;
            location / {
                proxy_pass http://tcsrvs;
                }
        }

Tomcat配置:
两台均需要安装

#yum install epel-release -y
#yum install java-1.8.0 java-1.8.0-openjdk-devel tomcat tomcat-webapps tomcat-admin-webapps tomcat-docs-webapp  -y

说明:也可以通过oracle官方下载jdk 下载tomcat 解压到指定目录并添加环境变量;一般企业推荐此种方式;为了快捷,我们用epel仓库中的稳定版本;

添加测试页
yum 安装的tomcat工作目录在/var/lib/tomcat/webapps 分别在node1与node2上,此目录下创建测试项目,title 分别叫Tomcat A与Tomcat B 颜色分别为green与red;以示区别;生产环境node1 与node2内容一致;这里为了测试区别node1与node2内容;

#mkdir -pv /var/lib/tomcat/webapps/test/{WEB-INF,META-INF,classes,lib}
#cat /var/lib/tomcat/webapps/test/index.jsp
<%@ page language="java" %>
 <html>
      <head><title>Tomcat Atitle>head>
          <body>
             <h1><font color="red">TomcatA.san.comfont>h1>
           <table align="centre" border="1">
            <tr>
                <td>Session IDtd>
            <% session.setAttribute("san.com","san.com"); %>
            <td><%= session.getId() %>td>
            tr>
            <tr>
            <td>Created ontd>
           <td><%= session.getCreationTime() %>td>
           tr>
      table>
    body>
html>

配置管理页密码
tomcat与管理程序安装好后配置访问密码
修改注释/etc/tomcat/tomcat-users.xml文件

<role rolename="admin-gui"/>
<role rolename="manager-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui,admin-gui"/>

备份默认/etc/tomcat/server.xml文件

cd /etc/tomcat
cp server.xml server.xml_def

测试页访问
http://www.san.com/test 如图:出现Tomcat A
Nginx+Tomcat+memcached高可用会话保持
Ctrl+F5强制刷新 又出现Tomcat B
Nginx+Tomcat+memcached高可用会话保持

引发问题:如果是两台内容一样的配置,客户端访问刷新一下就换到另一个后端处理;类似通过session保留信息的服务(购买物车)如何保留?换句话说,如何保持会话不中断,无论请求被分配到那一个后端?

解决方案
1)会话sticky(粘性):分为source_ip 基于源ip和cookie
source_ip在不同的调度器上有不同的实现方式:
lvs:sh算法;
nginx:ip_hash或hash $request_uri consistent(一致性哈希算法)
haproxy: source

cookie:
nginx:hash 或 hash $cookie_name consistent;
haproxy:cookie

2)会话集群(session cluster):delta session manager
3)session server: redis(store),memcached(cache)

以下基于tomcat自带会话集群与memcached实现会话保持 功能;

三、Tomcat Session Replication Cluster配置

Tomcat Session Replication Cluster中文又叫 tomcat 会话复制集群,即会话通过组播方式复制到每一个后端tomcat节点;
可参考自带帮助文档:http://www.san.com/docs/cluster-howto.html
两台node1 node2节点/etc/hosts中添加如下:

#cat /etc/hosts
192.168.10.11 node1.san.com node1
192.168.10.12 node2.san.com node2

两台tomcat 节点sever.xml的Host字段中添加如下内容:

"org.apache.catalina.ha.tcp.SimpleTcpCluster"
                 channelSendOptions="8">

          "org.apache.catalina.ha.session.DeltaManager"
                   expireSessionsOnShutdown="false"
                   notifyListenersOnReplication="true"/>

          "org.apache.catalina.tribes.group.GroupChannel">
            "org.apache.catalina.tribes.membership.McastService"
                        address="228.10.0.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            "org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="auto"           
                      port="4000"
                      autoBind="100"
                      selectorTimeout="5000"
                      maxThreads="6"/>

            "org.apache.catalina.tribes.transport.ReplicationTransmitter">
              "org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
            
            "org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
            "org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
          

          "org.apache.catalina.ha.tcp.ReplicationValve"
                 filter=""/>