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 "%r" %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) 编辑 收藏 举报