实现tomcat及Memcached、redis缓存服务器session共享
NoSQL
NoSQL是对非SQL、非传统关系型数据库的统称。
NoSQL一词诞生于1998年,2009年这个词汇被再次提出指非关系型、分布式、不提供ACID的数据库设计模式。
随着互联网时代的到来,数据爆发式增长,数据库技术发展日新月异,要适应新的业务需求。
随着移动互联网、物联网的到来,大数据的技术中NoSQL也同样重要。
https://db-engines.com/en/ranking
分类
Key-value Store
redis、memcached
Document Store
mongodb、CouchDB
Column Store列存数据库,Column-Oriented DB
HBase、Cassandra
Graph DB
Neo4j
Time Series 时序数据库
InfluxDB
Memcached不支持集群,只支持字符串类型、字节流,不支持持久化,不支持buffer缓存,而Redis支持字符串、源地址hash等类型,支持buffer缓存,且支持主从复制、集群、支持持久化等
Memcached只支持能序列化的数据类型,不支持持久化,基于Key-Value的内存缓存系统。
内存分配机制
应用程序运行需要使用内存存储数据,但对于一个缓存系统来说,申请内存、释放内存将十分频繁,非常容易导致大量内存碎片,最后导致无连续可用内存可用。
(1)Memcached采用了Slab Allocator机制来分配、管理内存。
(2)Page:分配给Slab的内存空间,默认为1MB,分配后就得到一个Slab。Slab分配之后内存按照固定字节大小等分成chunk。
(3)Chunk:用于缓存记录kv值的内存空间。Memcached会根据数据大小选择存到哪一个chunk中,假设chunk有128bytes、64bytes,数据只有100bytes存储在128bytes中,存在些浪费。
Chunk最大就是Page的大小,即一个Page中就一个Chunk
(5)Slab Class:Slab按照大小分组,就组成不同的Slab Class
如果有100bytes要存,那么Memcached会选择上图中Slab Class 2存储,因为它是120bytes的Chunk。
Slab之间的差异可以使用Growth Factor控制,默认1.25。
懒过期Lazy Expiration
memcached不会监视数据是否过期,而是在取数据时才看是否过期,过期的把数据有效期限标识为0,并不清除该数据。以后可以覆盖该位置存储其它数据。
LRU
当内存不足时,memcached会使用LRU(Least Recently Used)机制来查找可用空间,分配给新纪录使用。
集群
Memcached集群,称为基于客户端的分布式集群。
Memcached集群内部并不互相通信,一切都需要客户端连接到Memcached服务器后自行组织这些节点,并决定数据存储的节点。
memcached选项:
修改memcached运行参数,可以使用下面的选项修改/etc/sysconfig/memcached文件
-u username memcached运行的用户身份,必须普通用户 -p 绑定的端口,默认11211 -m num 最大内存,单位MB,默认64MB -c num 最大连接数,缺省1024 -d 守护进程方式运行 -f 增长因子Growth Factor,默认1.25 -v 详细信息,-vv能看到详细信息 -M 内存耗尽,不许LRU -U 设置UDP监听端口,0表示禁用UDP
实战一:基于session共享存储实现LAMT的会话保持(sticky模式生产中常用)
msm
msm(memcached session manager)提供将Tomcat的session保持到memcached或redis的程序,可以实现高可用。
目前项目托管在Github:https://github.com/magro/memcached-session-manager
支持Tomcat的6.x、7.x、8.x、9.x。
Tomcat的Session管理类,Tomcat版本不同
memcached-session-manager-2.3.2.jar memcached-session-manager-tc8-2.3.2.jar
Session数据的序列化、反序列化类
官方推荐kyro 在webapp中WEB-INF/lib/下
驱动类
memcached(spymemcached.jar) Redis(jedis.jar) # redis的jar包
官网下载依赖的jar包:https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration
将spymemcached.jar、memcached-session-manage、kyro相关的jar文件都放到Tomcat的lib目录中去,这个目录是$CATALINA_HOME/lib/ ,对应本次安装就是/usr/local/tomcat/lib。
asm-5.2.jar kryo-3.0.3.jar kryo-serializers-0.45.jar memcached-session-manager-2.3.2.jar memcached-session-manager-tc8-2.3.2.jar minlog-1.3.1.jar msm-kryo-serializer-2.3.2.jar objenesis-2.6.jar reflectasm-1.11.9.jar spymemcached-2.12.3.jar jedis-3.0.0.jar # redis相关的jar包
sticky模式
1、当请求结束时Tomcat的session会送给memcached备份。即Tomcat session为主session,memcached session为备session,使用memcached相当于备份了一份Session。
2、查询Session时Tomcat会优先使用自己内存的Session,Tomcat通过jvmRoute发现不是自己的Session,便从memcached中找到该Session,更新本机Session,请求完成后更新memcached。
<t1> <t2> . \ / . . X . . / \ . <m1> <m2>
t1和m1部署在一台主机上,t2和m2部署在同一台。
开始部署session共享会话保持
1、在t0和t1安装memcached服务
#yum install memcached -y
2、修改t0和t1的配置文件,需要监听其他地址的IP地址,否则只会监听本地的IP地址,内存可以设置为1024,最大连接设置为2048
[root@centos-7 yum.repos.d]# cat /etc/sysconfig/memcached PORT="11211" USER="memcached" MAXCONN="2048" #修改最大连接数 CACHESIZE="1024" # 修改内存大小 OPTIONS=""
3、启动memcached服务,监听的是11211端口
# systemctl start memcached
4、将依赖的包在官网下载下来并导入到/usr/local/tomcat/lib目录下:
asm-5.2.jar kryo-3.0.3.jar kryo-serializers-0.45.jar memcached-session-manager-2.3.2.jar memcached-session-manager-tc8-2.3.2.jar minlog-1.3.1.jar msm-kryo-serializer-2.3.2.jar objenesis-2.6.jar reflectasm-1.11.9.jar spymemcached-2.12.3.jar jedis-3.0.0.jar
5、修改tomcat服务的配置文件
特别注意,t0配置中为failoverNodes="n1(memcached1)", t1(tomcat1)配置为failoverNodes="n2(memcached2)"
failoverNodes故障转移节点,n1是备用节点,n2是主存储节点。另一台Tomcat将n1改为n2,其主节点是n1,备用节点是n2。
修改t0的配置文件
[root@mysql1 lib]# vim /usr/local/tomcat/conf/context.xml <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:192.168.7.100:11211,n2:192.168.7.101:11211" failoverNodes="n1" #改为n1时,n1为备用节点,优先使用n2的主机节点 requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />
修改t1的配置文件
[root@openstack-2 lib]# vim /usr/local/tomcat/conf/context.xml <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:192.168.7.100:11211,n2:192.168.7.101:11211" failoverNodes="n2" #改为n2时,优先使用n1的主节点,n2为备用节点。 requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />
6、修改tomcat(0和1)的主配置文件
#vim /usr/local/tomcat/conf/server.xml <Engine name="Catalina" defaultHost="t0.baidu.com" jvmRoute="Tomcat1"> #加上后面的名称,方便辨认是哪个主机 <Engine name="Catalina" defaultHost="t1.baidu.com" jvmRoute="Tomcat2">
7、修改两个tomcat的server.xml配置文件
# vim /usr/local/tomcat/conf/server.xml <Engine name="Catalina" defaultHost="t1.baidu.com"> </Host> <Host name="t1.baidu.com" appBase="/data/webapps" autoDeploy="true" > </Host> </Engine>
8、创建t0和t1的测试页面
# vim /data/webapps/ROOT/index.jsp <%@ page import="java.util.*" %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>lbjsptest</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>
9、启动tomcat服务:
# startup.sh
修改httpd服务的配置文件
修改httpd主配置文件
# vim /etc/httpd/conf/httpd.conf 注释 #DocumentRoot "/var/www/html"
修改httpd的反向代理配置
[root@computer-2 ~]# cat /etc/httpd/conf.d/vhosts.conf <VirtualHost *:80> ServerName node1.baidu.com ProxyRequests Off ProxyVia On ProxyPreserveHost On ProxyPass / balancer://lbtomcats/ ProxyPassReverse / balancer://lbtomcats/ </VirtualHost> <Proxy balancer://lbtomcats> BalancerMember ajp://t0.baidu.com:8009 loadfactor=1 route=Tomcat1 BalancerMember ajp://t1.baidu.com:8009 loadfactor=2 route=Tomcat2 #ProxySet stickysession=ROUTEID </Proxy>
启动httpd服务
# systemctl start httpd
测试效果:
第一次测试:
第二次测试:
下来模拟memcached的n2宕机,此时t0和t1都转移到memcached的n1上:
宕机n2:systemctl stop memcached
再次重启n2的memcached服务,此时Tomcat1就会又去优先到n2进行写入缓存:
重启n2的memcached服务:systemctl restart memcached
实战二:基于session共享存储实现LNMT的会话保持(sticky模式生产中常用)
1、修改nginx服务器的主配置文件
upstream tomcats { #在http 段配置 server t0.baidu.com:8080 weight=1; server t1.baidu.com:8080 weight=2; } location / { #在server 段配置 proxy_pass http://tomcats; } location ~* \.(jsp|do)$ { proxy_pass http://tomcats; }
只需要将httpd的配置改为nginx配置,upstream转发到后端服务器,其他的tomcat配置不变、memcached服务配置文件都在上面:
2、测试效果:
此时tomcat1对应的n2和tomcat2对应的n1都是一个session ID,实验成功:
实战三:基于session共享存储(memcached)实现LNMT的会话保持(non-sticky模式)
实现原理
从msm 1.4.0之后开始支持non-sticky模式。
Tomcat session为中转Session,n1为主session,n2为备session。产生的新的Session会发送给主、备memcached,并清除本地Session。
n1下线,n2转正。n1再次上线,n2依然是主Session存储节点。
注意:其他测试页面在上面已经存在,下面就不在写入。
1、开始部署,在t0和t1的/usr/local/tomcat/conf/context.xml配置文件中修改
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:192.168.7.100:11211,n2:192.168.7.101:11211" sticky="false" sessionBackupAsync="false" lockingMode="uriPattern:/path1|/path2" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />
2、修改nginx服务器的主配置文件
upstream tomcats { #在http 段配置 server t0.baidu.com:8080 weight=1; server t1.baidu.com:8080 weight=2; } location / { #在server 段配置 proxy_pass http://tomcats; } location ~* \.(jsp|do)$ { proxy_pass http://tomcats; }
3、启动nginx服务和tomcat服务
# nginx # shutdown.sh # startup.sh
测试效果
此时可以看到,访问tomcat时,占用的是memcached的n1
模拟此时的n1(memcached)宕机,此时n2就会占用上来,由于n1和n2是同优先级的服务器,此时就算n1启动起来,也不会再占用到n1的缓存服务器上。
实战四:基于session共享存储(redis)实现LNMT的会话保持(non-sticky模式)
1、安装并配置redis服务
# yum install redis -y # vim /etc/redis.conf #修改redis配置 bind 0.0.0.0 #监听本地的所有IP地址 # systemctl start redis #启动redis服务
2、配置tomcat服务
修改tomcat文件时,目前没有做redis主从复制及集群模式,生产中必须做,此时将IP地址都指向了一个redis的IP地址上
[root@centos-7 ~]# vim /usr/local/tomcat/conf/context.xml <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="redis://192.168.7.100:6379" sticky="false" sessionBackupAsync="false" lockingMode="uriPattern:/path1|/path2" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" />
创建t0和t1的测试页面
# vim /data/webapps/ROOT/index.jsp <%@ page import="java.util.*" %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>lbjsptest</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>
3、修改nginx服务器的主配置文件
upstream tomcats { #在http 段配置 server t0.baidu.com:8080 weight=1; server t1.baidu.com:8080 weight=2; } location / { #在server 段配置 proxy_pass http://tomcats; } location ~* \.(jsp|do)$ { proxy_pass http://tomcats; }
启动nginx和tomcat服务
# nginx # shutdown.sh # startup.sh
测试效果:
两个主机的session ID都一致,实验成功:
总结
通过多组实验,使用不同技术实现了session持久机制
1. session绑定,基于IP或session cookie的。其部署简单,尤其基于session黏性的方式,粒度小,对负载均衡影响小。但一旦后端服务器有故障,其上的session丢失。
2. session复制集群,基于tomcat实现多个服务器内共享同步所有session。此方法可以保证任意一台后端服务器故障,其余各服务器上还都存有全部session,对业务无影响。但是它基于多播实现心跳,TCP单播实现复
制,当设备节点过多,这种复制机制不是很好的解决方案。且并发连接多的时候,单机上的所有session占据的内存空间非常巨大,甚至耗尽内存。
3. session服务器,将所有的session存储到一个共享的内存空间中,使用多个冗余节点保存session,这样做到session存储服务器的高可用,且占据业务服务器内存较小。是一种比较好的解决session持久的解决方案。
以上的方法都有其适用性。生产环境中,应根据实际需要合理选择。
不过以上这些方法都是在内存中实现了session的保持,可以使用数据库或者文件系统,把session数据存储起来,实现持久化。
这样服务器重启后,也可以重新恢复session数据。不过session数据是有时效性的,是否需要这样做,视情况而定。