Session会话保持机制的原理与Tomcat Session共享的几种实现方式(Session Cluster、memcached+MSM)
一、Session的定义
在计算机科学中,特别是在网络中,session是两个或更多个通信设备之间或计算机和用户之间的临时和交互式信息交换。session在某个时间点建立,然后在之后的某一时间点拆除。建立的通信session可以在每个方向上涉及多个消息。session通常是有状态的,这意味着至少一个通信部分需要保存关于会话历史的状态信息以便能够进行通信,而在无状态通信中,通信由具有响应的独立请求组成。——Wikipedia
Session:在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在 Session 对象中。——百度
二、Session的作用与意义
在WEB应用中,一般都是采用http协议进行通信,但http是一种无状态的通信协议,这里的无状态是指协议对于事务处理没有记忆能力,在会话结束后,服务器不知道客户端是什么状态。在http协议初期,为了保证WEB的并发访问处理能力,http协议不会保存客户端访问的任何状态信息。后来http协议中加入了keep-alive功能,这是一种会话保持机制,这样便可以将会话持续保持一段时间,当用户在这个时间段又一次访问时会提高响应速度节省资源,但keep-alive也不会记录用户的状态,只是让连接保持一段时间,当用户超过这个时间段再次访问,依然还是会当做新请求进行处理。但在WEB应用中有很多场景都需要根据用户的历史访问状态来将请求分配给后端服务器处理,例如登录记录、购物车记录,浏览记录等,用户每次访问都无法找到以前的记录是很影响用户体验的,这样就诞生了cookie和session机制。
先来说说cookie,当用户访问www.abc.com时需要登录,因为http是无状态的,所以导致访问www.abc.com/xxx又需要重新登录,这样是很头痛的事情,cookie的出现解决了这个问题,cookie可以将用户的少量信息保存至用户客户端本地,它将www.abc.com域名作为一个全局,在用户登录此域名下的所有URI时都不需要重新登陆。但用户本地的存储大小是有限的,并且将一些私密信息储存在本地是有安全风险的。
如何保证用户体验,又能保证安全性,这就是Session的意义,session也是一种用户状态储存机制,但与cookie不同的是,session储存在后端服务器的内存中。session在web应用中有着非常重要的意义,它可以很方便很安全的控制访问权限。
二、常见的Session会话保持方式
1.Session sticky(session会话粘性)
会话粘性是指在用户第一次访问后按一定的算法与之将其中一台后端服务器做绑定,一般分为两种:
a.souce_ip:如nginx中的ip_hash算法、LVS中的sh算法
b.cookie:HAProxy中的cookie
2.Session Cluster(session集群)
Session Cluster是指将后端服务器组成一个集群,共享所有的session, 当某一台服务器故障后不至于影响用户体验。这种方法一般用在后端服务器较少的情况(3-4台),因为当服务器太多时会占用大量的IO,影响集群性能。
下面将以Tomcat为例介绍介绍这种session保持机制。
1.实现基础
Tomcat Session Cluster的实现是利用组播将集群中的session进行共享,这与keepalived类似。Tomcat原生就支持这种机制,所以配置起来比较简单,但不能支持太多的主机,
2.结构与IP分配
Nginx主机:IP:192.168.29.109
Tomcat主机集群:192.168.29.107、192.168.29.132、192.168.29.110
安装好Nginx和Tomcat,教程:https://www.cnblogs.com/readygood/p/9801253.html
关闭防火墙和Selinux
1.配置Nginx
vim /etc/nginx/nginx.conf #配置主配置文件
upstream tcsv { #在http模块中插入upstream模块 server 10.10.10.130:8080; server 10.10.10.131:8080; server 10.10.10.132:8080; }
vim /etc/nginx/conf.d/tomcat.conf #添加虚拟主机
server { listen 80; server_name www.ready.com; location / { proxy_pass http://tcsv; } }
2.配置Tomcat,创建.jsp测试页
mkdir -pv /var/lib/tomcat/webapps/test/{WEB-INF,META-INF,classes,lib} #创建jsp文件所需目录
vim /var/lib/tomcat/webapps/test/index.jsp #创建jsp测试文件,功能为session ID监控
<%@ page language="java" %> <html> <head><title>TomcatA</title></head> <body> <h1><font color="red">Tomcat.ready.com</font></h1> #将3台Tomcat主机配置为不同颜色,我这里分别是red、green、blue <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("ready.com","ready.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
3.启动Nginx和Tomcat,并刷新页面(修改hosts添加本地解析)
不同颜色代表不同主机的响应,我们可以发现就算响应的主机相同Session ID也不同,这说明Session机制未开启(因为前端Nginx调度默认采用的rr算法),就算在同一主机访问也会被识别为不同用户。
4.配置Tomcat Cluster服务
Tomcat原生就是支持此服务的,所以只需要在/etc/tomcat/server.xml中嵌入 <Cluster> 组件即可,要注意的是 <Cluster> 组件只能用在 <Engine> 和 <Host> 中。注意事项如下:
1.默认多播地址为228.0.0.4
2.默认多播端口是45564(端口和地址一起决定集群成员。
3.广播的IP是 java.net.InetAddress.getLocalHost().getHostAddress() (确保没有广播127.0.0.1,这是一个常见的错误)
4.监听复制消息的TCP端口是范围4000-4100中的第一个可用服务器套接字
5.侦听器被配置为 ClusterSessionListener
6.配置两个拦截器 TcpFailureDetector 和 MessageDispatch15Interceptor
vi /etc/tomcat/server.xml
a.将下面的代码插入 <Engine> 或者 <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.0.4" #默认组播地址 port="45564" #默认端口 frequency="500" dropTime="3000"/> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="192.168.29.132" #填写本机IP地址,默认为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.JvmRouteSessionIDBinderListener"/> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> </Cluster>
b.将默认web.xml文件(/etc/tomcat/web.xml)复制到事先创建好的/usr/share/tomcat/webapps/test/WEB-INF/目录下并修改
web.xml文件是用来初始化配置信息:比如Welcome页面、servlet、servlet-mapping、filter、listener、启动加载级别等。
cp /etc/tomcat/web.xml /usr/share/tomcat/webapps/test/WEB-INF/
vim /usr/share/tomcat/webapps/test/WEB-INF/web.xml
在 <web-app> 内任意位置插入代码 <distributable/> ,其他Tomcat主机也做相同操作。
c.重启Tomcat服务,查看Tomcat状态
在日志中看到下面的信息就说明session共享配置成功:
d.刷新页面,观察session ID的变化
可以看到不管怎么刷新Session ID都保持不变,但标题颜色会变化,说明无论去前端Nginx怎么调度Session都没有发生变化。
e.注意事项
1.在插入 <Cluster> 组件时要注意 <Receiver> 中的 address= 的IP配置,尽量不要用“auto”。
2.Tomcat主机的网关一定要设置正确,不然无法发送和接收组播信息。
3.要在日志中看到类似 [tcp://{192, 168, 29, 107}:4000,{192, 168, 29, 107},4000, alive=1037 的信息才说明组播信息成功发送,也可以使用 tcpdump 命令来检查组播信息状态:
tcpdump -i ens33 -nn host 228.0.0.4
3.Tomcat+memcached+msm实现Session共享
在Tomcat Session Cluster方式实现session共享时,后端不能承载太多的Tomcat主机,只适用于小型的集群使用,为了克服这一问题,我们可以将后端的Tomcat集群的所有session都存放在一个共享的存储中,这样不管用户请求被反代到哪一个后端服务器都不会影响体验。
1.memcached简介
Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。memcached特性:
1.仅可用来存储可序列化的数据。
2.分布式缓存:互不通信的分布式集群。
3.数据存储在内存中,重启即丢失数据。
4.缓存耗尽:LRU
5.惰性清理机制。
3.安装memcached
memcached已经收录在yum的base仓库,安装起来非常简单。
~]# yum install -y memcached
查看memcached的配置文件
~]# cat /etc/sysconfig/memcached
PORT="11211" #监听端口 USER="memcached" #用户名 MAXCONN="1024" #最大并发连接数 CACHESIZE="64" #缓存空间大小,默认为64MB OPTIONS=""
memcached常用选项:
memcached程序的常用选项:
-m <num>:Use <num> MB memory max to use for object storage; the default is 64 megabytes.
-c <num>:Use <num> max simultaneous connections; the default is 1024.
-u <username>:以指定的用户身份来运行进程;
-l <ip_addr>:监听的IP地址,默认为本机所有地址;
-p <num>:监听的TCP端口, the default is port 11211.
-U <num>:Listen on UDP port <num>, the default is port 11211, 0 is off.
-M:内存耗尽时,不执行LRU清理缓存,而是拒绝存入新的缓存项,直到有多余的空间可用时为止;
-f <factor>:增长因子;默认是1.25;
-t <threads>:启动的用于响应用户请求的线程数;
4.Memcached Session Manager(MSM)安装
MSM是一个将Tomcat Session存储在memcached中的工具,这样就能克服Tomcat自建的session共享机制后端服务器的数量限制。
试验结构图:
5.下载MSM所需.jar文件并放在Tomcat安装目录的lib目录下
我这里是yum安装的,目录是在/usr/share/java/tomcat/下,需要复制到该目录的文件如下(两台Tomcat主机操作相同),这一步很重要,如果文件版本不对会导致后面session共享不成功,我所用到的版本如下:
memcached-session-manager-1.8.2.jar memcached-session-manager-tc7-1.8.2.jar msm-javolution-serializer-1.8.2.jar spymemcached-2.10.2.jar javolution-5.5.1.jar
放在 /usr/share/java/tomcat/ 目录下
6.在/etc/tomcat/server.xml默认配置内的<Host>上增加<Context>
在两台Tomcat主机上修改相同配置
vim /etc/tomcat/server.xml
<Host ......
<Context path="/test" docBase="test" reloadable="true"> <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:192.168.29.107:11211,n2:192.168.29.110:11211" failoverNodes="n1" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory" /> </Context>
......
</Host>
7.创建java测试项目
192.168.29.107主机上的配置:
mkdir -pv /usr/share/tomcat/webapps/test/WEB-INF/{classes,lib} vim /usr/share/tomcat/webapps/test/index.jsp
<%@ page language="java" %> <html> <head><title>TomcatB</title></head> <body> <h1><font color="blue">Tomcat.Ready.com</font></h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("ready.com","ready.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
192.168.29.110主机上的配置:
mkdir -pv /usr/share/tomcat/webapps/test/WEB-INF/{classes,lib} vim /usr/share/tomcat/webapps/test/index.jsp
<%@ page language="java" %> <html> <head><title>TomcatB</title></head> <body> <h1><font color="red">Jerrmouse.Ready.com</font></h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("ready.com","ready.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
8.启动Tomcat和memcached,并查看是否正常运行
systemctl start tomcat
systemctl start memcached
ss -lntup
完成上述步骤后浏览器访问显示如下:
Session ID后面多出了n1字符,并且不断刷新页面Session ID都不会改变,说明MSM+Tomcat的session共享配置成功。
再强调一次,复制到/usr/share/java/tomcat/的jar文件版本很重要。