Tomcat Session Replication Cluster和MSM的non-sticky模式解决方案

前言:
1)Nginx、Tomcat已事先安装。
Nginx安装路径: /apps/nginx
Tomcat安装路径:/usr/local/Tomcat 
2)Nginx作为对外的调度器,将用户的动态请求调度到后端Tomcat服务集群(即实现动静分离与负载均衡)
3)Tomcat 官方实现了 Session 的复制集群,将每个Tomcat的Session进行相互的复制同步,从而保证所有Tomcat都有相同的Session信息.缺点:Tomcat负载大不适合大规模场景。
官方文档:

https://tomcat.apache.org/tomcat-9.0-doc/cluster-howto.html 

1.为T1、T2 创建虚拟主机以及集群配置(方便后期测试session的绑定情况)
1)文件路径:/usr/local/Tomcat/conf/server.xml
2)标红是虚拟主机和集群配置(增加)
3)node1.qinglin.com 是创建虚拟主机的域名(T1),T2的话可自定义不过我这边设置为 node2.qinglin.com 好记一点。
4)/data/webapps  为虚拟主机的应用目录
5)node1.qinglin.com_access_log  自定义的独有日志名称

 <Host name="node1.qinglin.com" appBase="/data/webapps" unpackWARs="false" autoDeploy="false">   #以域名创建虚拟主机及指定虚拟主机的应用目录
         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                prefix="node1.qinglin.com_access_log" suffix=".log"
          pattern="%h %l %u %t &quot;%r&quot; %s %b" />

<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="10.0.0.200"  #改为T1、T2的IP
                      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.MessageDispatchInterceptor"/>
          </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>

      </Host>

 

 

 

 

2.T1、T2创建应用目录及其下的根目录。

mkdir /data/webapps/ROOT/ -p 

3.将localhost的 WEB-INF 拷贝至T1、T2应用目录下的根目录中

cp -a /usr/local/tomcat/webapps/ROOT/WEB-INF /data/webapps/ROOT/WEB-INF

 4.修改web.xml文件,允许session复制

vim /data/webapps/ROOT/WEB-INF/web.xml <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> <distributable/> #此位置,添加此项 </web-app> 

5.在ROOT目录下创建用于观察session情况的index.jsp文件。

 

cat /data/webapps/ROOT/index.jsp

<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <title>tomcat test</title>
</head>
<body>
<div>On <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html>

 

 

 

注:查看T1、T2应用目录下的文件所有者所属组是否为Tomcat,无误后重启Tomcat服务。

6.配置Nginx负载均衡服务集群。
1)将T1、T2基于域名所创建的虚拟主机及提供服务的8080端口加入服务集群。

vim /apps/nginx/conf/nginx.conf http { upstream tomcat-server { server node1.qinglin.com:8080; server node2.qinglin.com:8080; } 

 2)用location语句块匹配jsp(动态请求)将其代理到Tomcat集群,实现一个简单的动静分离。

location ~* \.jsp { proxy_pass http://tomcat-server;

 

3)因T1、T2的虚拟主机是通过自定义域名所创建,因此Nginx服务器本机需要添加该域名的解析

vim /etc/hosts 10.0.0.200 node1.qinglin.com 10.0.0.201 node2.qinglin.com 

注:通过ngixn -t 命令检查语法是否出错后重启Nginx服务。|

7.在浏览器通过Ngixn调度器访问T1、T2用于测试index.jsp文件看是否能够成功调度请求。
1)可以看到,即便通过服务集群的rr算法调度请求到不同的Tomcat服务器但session仍然能够绑定

 

 

2)将10.0.0.200(T1)上的Tomcat服务关闭模拟宕机,看session是否仍然能够保持。
<关闭后Tomcat后,因为Nginx负载均衡具备健康性检查功能,即便不往T1调度,但session仍然保持了下来实现了冗余>

注:出现此页面而并非是编写的测试文件虚拟主机index.jsp,则是因为jsp请求交由了Tomcat服务器的localhost处理不并非创建出来的虚拟机且由于Nginx调度器的设置只调度jsp请求,导致localhost主页面除了jsp文件之外其他的css等无法显示出来。
解决方法:在Engine指令这里将默认处理请求的主机改为虚拟主机



————————————————————————————————————————————————————————

msm(memcached session manager)提供将Tomcat的session保持到memcached或redis的程序,可以实现高可用。
项目早期托管在google code,目前在Github,github网站链接: https://github.com/magro/memcached-session-manager

 

 

 

Tomcat扩展jar包:

将spymemcached.jar、memcached-session-manage、kyro相关的jar文件都放到Tomcat的lib目录中去,这个目录是 $CATALINA_HOME/lib/ ,对应本次安装就是/usr/local/tomcat/lib。

 

kryo-3.0.3.jar
asm-5.2.jar
objenesis-2.6.jar
reflectasm-1.11.9.jar
minlog-1.3.1.jar
kryo-serializers-0.45.jar
msm-kryo-serializer-2.3.2.jar
memcached-session-manager-tc8-2.3.2.jar
spymemcached-2.12.3.jar
memcached-session-manager-2.3.2.jar

non-sticky 模式工作原理:
non-sticky 模式即前端tomcat和后端memcached无关联(无粘性)关系
从msm 1.4.0之后版本开始支持non-sticky模式。

Tomcat session为中转Session,对每一个SessionID随机选中后端的memcached节点M1(或者M2)为主session,而另一个memcached节点M2(或者是M1)为备session。产生的新的Session会发送给主、备memcached,并清除本地Session(清除Tomcat服务器上生成的session,以此减少内存消耗(non-sticy模式好用一点,几乎不存在丢失session的问题)。后端两个memcached服务器对一个session来说是一个是主,一个是备,但对所有session信息来说每个memcached即是主同时也是备,如果M1下线,M2则转正。M1再次上线,M2依然是主Session存储节点。

补充:
1)为什么采用non-sticy模式?sticky需要M1或M2对端的T1或T2也保存一份相同的session,这就会增大tomcat服务器的负载。
2)sticky模式建议t服务器和m服务器分开部署,假设T1、M1同一台机器S1   T2、M2同一台机器S2 ,由T1分配给Client请求session保存至M2,而S1宕机而调度器具备健康性检查功能将Client请求调度到T2然后从M1获取session(M1本身没有保存client的session但会从M2获取),但此刻因为S1的宕机导致M1无法提供服务从而无法获取session致使Client的会话无法保存。
(sticky是对端存储架构)

non-sticky模式架构图:

 

 

 

 

解读:
Client连接请求被Nginx调度器调度到Tomcat服务集群(T1、T2),T1、T2生成并分配session以及挑选M1或M2作为master节点另外一个作为slave节点。随后向master节点发送生成的session,master节点收到后将session向slave复制过去,最后T1或T2将生成的session清除掉。

前述:负载均衡和tomcat虚拟主机可参考 Tomcat Session Replication Cluster方案

1.Nginx jDK Tomcat Memcached 等基本软件已安装。
2.Tomcat已基于域名创建虚拟主机T1、T2

3.Nginx已将T1、T2加入到负载均衡服务集群中并添加解析。
1)T1和T2虚拟主机加入到负载均衡集群。

2)通过location语句块匹配后缀为jsp等动态请求(动静分离的概念)

3)添加T1、T1虚拟主机的域名解析

 

第一步:msm需要的jar包下载后置于T1、T2 /usr/local/tomcat/lib下 

 

第二步:将测试index.jsp文件置于T1、T2的ROOT目录下以做测试。

cat /data/webapps/ROOT/index.jsp

<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
        <meta charset="UTF-8">
        <title>tomcat test</title>
</head>
<body>
<div>On <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html>

 

 

第三步:修改T1、T2的 context.xml文件,添加memcache地址(T1与T2配置一样)

        <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
        memcachedNodes="m1:10.0.0.202:11211,m2:10.0.0.203:11211"   #指定m1和m2的地址
        sticky="false"  #将粘性模式关闭采用非粘性
        sessionBackupAsync="false"
        lockingMode="uriPattern:/path1|/path2"
        requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
        transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
        />

 

 

第四步:重启tomcat服务,监察日志观看节点是否生效(T1、T2都要看

tail -f /usr/local/tomcat/logs/catalina.out

-  finished initialization:
- sticky: false
- operation timeout: 1000
- node ids: [m1, m2]
- failover node ids: []
- storage key prefix: null
- locking mode: uriPattern:/path1|/path2 (expiration: 5s) 

 

验证:
1.通过浏览器访问Nginx调度器的域名模拟对外提供服务。注:需在本机添加Nginx调度器的域名解析才能进行模拟

 

2.输入域名,URL为 index.jsp 这个用于观测tomcat分配给客户端SessionID的文件.
<可以看到请求被tomcat-server服务集群调度到了后端的t1和t2且刷新页面后session保持不变><会话绑定基本实现>

 

 

 

 

3.在Nginx调度器上编写测试脚本用来观测memcache是否已经缓存T1或T2发送过来的SessionID
<运行后看到Memcache已经缓存了T1或T2发送过来的SessionID且Master:M2、Slave:m1并进行主从复制
>

cat showmemcached.py

#!/usr/bin/python3
import memcache # pip3 install python3_memcached
mc = memcache.Client(['10.0.0.202:11211','10.0.0.203:11211'], debug=True)
#print('-' * 30)
# 查看全部key
#for x in mc.get_stats('items'):  # stats items 返回 items:5:number 1
#    print(x)
print('-' * 30)

for x in mc.get_stats('cachedump 4 0'):
    print(x)

注意事项:
1)安装 pip3
2)安装python39
3)导入模块  python3_memcached
4)如果提示相关模板要安装记得安装。
5)index.jsp 观测的是tomcat分配给客户端SessionID的情况而showmemcached.py  观测的是Memcache是否已经缓存了Tomcat服务器发送过来的SessionID


4.M2作为Master进行sessionID的主从复制,那么模拟故障M2宕机,倘若SessionID不变化则说明主从复制成功。

systemctl stop memcached.service


可以看到SessioID不变但提供Session信息的已经变成了M1,成功!

 

posted on 2021-09-06 19:58  1251618589  阅读(10)  评论(0编辑  收藏  举报

导航