Tomcat之session解决方案
Tomcat集群使用Memcached实现Session共享
session方案及配置
用户访问服务器资源主要分成两类,一类是无状态访问,例如请求一张图片。另一类是有状态访问,这种情况下,服务器需要记录追踪用户状态,并根据用户所处状态做出不同响应,典型的例子是购物车。Session的作用就是在Web服务器上保持用户的状态信息。
用户请求连接服务器时,服务器会生成一个唯一的sessionID为标识符至用户端本地,客户端使用该sessionID来存/取服务器端的session空间。sessionID是客户端浏览器cookie保存的。
当客户端访问Tomcat集群时,所有的请求将被Nginx拦截,由Nginx做负载均衡后转发给后台真实Tomcat。按照这个流程就可能出现一个问题,当用户进行页面刷新或跳转时,每次请求将被转发给不同的Tomcat处理,这样就会造成Session的不同步。举个简单的栗子,例如当用户往购物车添加商品时,兴高采烈地准备买单了,当他跳转到付款页面却发现购物车被清空了,这就是Session丢失的典型栗子。因此,我们需要为集群环境做Session同步。
总结一下:session就是在一次会话中解决2次(或多次)http请求的关联,让他们产生联系,让2个(或多个)页面都能读取到这个全局的session信息,session信息存在于服务端,所以也就解决了session的问题。。而我们现在所说的session解决方案,一般是指怎么避免一台后端服务器挂掉,还能正常保持session
单机环境下,session可有部署在服务器上的web容器如:Tomcat进行保存管理。但在使用负载均衡集群时如架构前端Nginx来负载均衡后端多台Tomcat,所以可能分发到任何一台后端的Tomcat;虽然呢,也有类似于Nginx中的ip_hash算法可以将客户端和服务器做一个绑定,但是弊端太多,生产环境慎用。
下面将介绍几种session的方案
一、session绑定
session绑定就是利用负载均衡服务器的hash源IP地址算法实现,将来源于同一个IP的请求总是分发到同一台后端web服务器上,又称为会话粘滞。
但是如果该服务器死机或故障,那该用户的session空间也就不复存在了,就如我们网页浏览购物网站时,刚添加到购物车的宝贝,一刷新全没了,用户体验肯定非常差,所以该session绑定方案使用场景非常有限
#编辑nginx主配置文件
upstream tomcats {
ip_hash
#使用ip_hash算法调度
server 192.168.111.4:8080;
server 192.168.111.5:8080;
}
二、session复制
适用于小型架构的服务器集群。开启web服务器的session复制功能,在集群中的几台服务器之间同步session对象,这样每台服务器都保存了用户的session信息,但是当集群规模比较大时,session复制机制会消耗大量系统资源以及网络资源
操作系统 | IP地址 | 软件版本 | 主机名 |
---|---|---|---|
centos7 | 192.168.111.3 | nginx1.14.2 | Nginx |
centos7 | 192.168.111.4 | JDK1.7;Tomcat7.0.54 | tnode1 |
centos7 | 192.168.111.5 | JDK1.7;Tomcat7.0.54 | tnode2 |
如有防火墙或selinux记得关闭或者修改相应规则
[root@localhost ~]# nginx -v
nginx version: nginx/1.14.2
[root@localhost ~]# vim /etc/hosts
192.168.111.3 nginx
192.168.111.4 tnode1
192.168.111.5 tnode2
[root@localhost ~]# scp /etc/hosts 192.168.111.4:/etc/
[root@localhost ~]# scp /etc/hosts 192.168.111.5:/etc/
#配置hosts文件
#hostname nginx
#hostname tnode1
#hostname tnode2
修改主机名
[root@nginx ~]# vim /usr/local/nginx/conf/nginx.conf
http {
...
upstream tomcatpool {
server 192.168.111.4:8080 weight=1 max_fails=1 fail_timeout=10s;
server 192.168.111.5:8080 weight=1 max_fails=1 fail_timeout=10s;
}
#以上为添加
location / {
root html;
index index.html index.htm;
proxy_pass http://tomcatpool;
}
#以上为修改添加最下面一行调用池
安装配置Tomcat。
[root@tnode1 ~]# tar zxf jdk-7u65-linux-x64.tar.gz
[root@tnode1 ~]# mv jdk1.7.0_65/ /usr/local/java7
[root@tnode1 ~]# echo "PATH=$PATH:/usr/local/java7/bin" >> /etc/profile
[root@tnode1 ~]# source /etc/profile
[root@tnode1 ~]# rm -rf /usr/bin/java
[root@tnode1 ~]# java -version
java version "1.7.0_65"
Java(TM) SE Runtime Environment (build 1.7.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
[root@tnode1 ~]# tar zxf apache-tomcat-7.0.54.tar.gz
[root@tnode1 ~]# mv apache-tomcat-7.0.54 /usr/local/tomcat7
[root@tnode2 ~]# tar zxf jdk-7u65-linux-x64.tar.gz
[root@tnode2 ~]# mv jdk1.7.0_65/ /usr/local/java7
[root@tnode2 ~]# echo "PATH=$PATH:/usr/local/java7/bin" >> /etc/profile
[root@tnode2 ~]# source /etc/profile
[root@tnode2 ~]# rm -rf /usr/bin/java
[root@tnode1 ~]# java -version
java version "1.7.0_65"
Java(TM) SE Runtime Environment (build 1.7.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
[root@tnode2 ~]# tar zxf apache-tomcat-7.0.54.tar.gz
[root@tnode2 ~]# mv apache-tomcat-7.0.54 /usr/local/tomcat7
[root@tnode1 ~]# vim /usr/local/tomcat7/webapps/ROOT/session.jsp
#测试页面
Session ID:<%= session.getId() %><BR>
SessionPort:<%= request.getServerPort() %>
<% out.println("This tomcat server 192.168.111.4");%>
[root@tnode2 ~]# vim /usr/local/tomcat7/webapps/ROOT/session.jsp
Session ID:<%= session.getId() %><BR>
SessionPort:<%= request.getServerPort() %>
<% out.println("This tomcat server 192.168.111.5");%>
#这是一个获取当前服务器所拥有的IP和sessionID的脚本页面
下面开始配置session复制(生产环境时一般10台以下使用该session解决方案
[root@tnode1 ~]# vim /usr/local/tomcat7/conf/server.xml
104 <Engine name="Catalina" defaultHost="localhost" jvmRoute="tnode1">
#改行后面添加jvmroute配置项。
109 <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
#去掉注释
[root@tnode1 ~]# vim /usr/local/tomcat7/webapps/ROOT/WEB-INF/web.xml
</description>
#添加至倒数第二行
[root@tnode2 ~]# vim /usr/local/tomcat7/conf/server.xml
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tnode2">
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
[root@tnode1 ~]# vim /usr/local/tomcat7/webapps/ROOT/WEB-INF/web.xml
</description>
#添加至倒数第二行
[root@tnode1 ~]# /usr/local/tomcat7/bin/shutdown.sh
[root@tnode1 ~]# /usr/local/tomcat7/bin/startup.sh
[root@tnode2 ~]# /usr/local/tomcat7/bin/shutdown.sh
[root@tnode2 ~]# /usr/local/tomcat7/bin/startup.sh
客户端使用火狐浏览器输入:http://192.168.111.3/session.jsp来进行测试,并且刷新(别强制刷新,那样是新的sessionID)
三、使用memcached解决session问题
memcached是一套分布式的快取系统,相关数据都是在内存里,一旦服务重启或者死机,则数据必然丢失;memcached是键值对存储形式;在以下试验中,每个session都会在这两台memcached上进行分布式存储,有了冗余性,即使一台出问题也不影响工作。同样只适用于中小型架构。
以下的配置两台Tomcat一样
[root@tnode1 ~]# yum -y install libevent memcached
#安装memcached及其依赖
[root@tnode1 ~]# memcached -u root -m 512M -n 10 -f 2 -d -vvv -c 512
//-u:运行用户必须是root身份
//-m:指定使用物理机的多少内存
//-n:chunk size的最小空间是多少字节
//-f:chunk size大小增长的倍数默认1.25倍
//-d:在后台启动
//-vvv:显示详细信息
//-c:memcached服务的最大连接数
Tomcat连接memcached所依赖的库文件:
javolution-5.5.1.jar memcached-session-manager-1.5.1.jar msm-kryo-serializer-1.6.4.jar
kryo-1.03.jar memcached-session-manager-tc7-1.5.1.jar reflectasm-0.9.jar
kryo-serializers-0.10.jar minlog-1.2.jar spymemcached-2.7.3.jar
memcached-2.5.jar msm-javolution-serializer-1.5.1.jar
将这些文件放到/usr/local/tomcat7/lib/目录下
配置文件连接memcached。
[root@tnode2 ~]# vim /usr/local/tomcat7/conf/context.xml
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="memA:192.168.111.4:11211 memB:192.168.111.5:11211" requestUrilgnorePattern=".*\(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"/>
[root@tnode2 ~]# /usr/local/tomcat7/bin/shutdown.sh
[root@tnode2 ~]# /usr/local/tomcat7/bin/startup.sh
#重启服务
测试页仍然使用以上的那个测试页
浏览器访问http://192.168.111.3/session.jsp测试,可以看到sessionID不变
四、使用redis解决session问题
注意:尽量保持系统环境的纯净
如果使用如上做完memcached环境接着做redis
1.rpm -e memcached-1...
2.vim /usr/local/tomcat7/conf/context.xml
3.pkill -9 memcached
4.删除相关jar包
安装部署redis
[root@tnode1 ~]# tar zxf redis-3.2.5.tar.gz
[root@tnode1 ~]# yum -y install tcl
#依赖包
[root@tnode1 src]# mkdir /usr/local/redis/{bin,etc,var} -pv
[root@tnode1 src]# cp ~/redis-3.2.5/src/redis-benchmark redis-check-aof redis-cli redis-server /usr/local/redis/bin/
//redis-benchmark:读写性能测试工具
//redis-cli:redis命令行操作工具
//redis-server:redis服务的daemon启动程序
[root@tnode1 src]# cp ~/redis-3.2.5/redis.conf /usr/local/redis/etc/
//redis的主配置文件
[root@tnode1 src]# vim /usr/local/redis/etc/redis.conf
128 daemonize yes
#表示将redis启动在后台
61 bind 0.0.0.0
#监听所有主机
[root@tnode1 src]# /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
[root@tnode1 src]# netstat -anpt | grep redis
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 69432/redis-server
#启动并且查看端口
Tomcat和redis连接需要用到如下包:
commons-logging-1.1.3.jar jedis-2.5.2.jar tomcat-redis-session-manage-tomcat7.jar
commons-pool2-2.2.jar tomcat-juli.jar
[root@tnode1 ~]# vim /usr/local/tomcat7/conf/context.xml
#连接redis配置
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" host="192.168.111.4" port="6379" database="0" maxInactiveInterval="60" />
#Context字段中添加如上,IP为redis服务器的IP。
#如下可以先把node2上该lib目录删掉,直接复制覆盖
[root@tnode1 ~]# scp -r /usr/local/tomcat7/lib root@192.168.111.5:/usr/local/tomcat7/lib
#如下是直接覆盖node2配置文件
[root@tnode1 ~]# scp /usr/local/tomcat7/conf/context.xml root@192.168.111.5:/usr/local/tomcat7/conf/
#然后node2上重启Tomcat服务,可多重启几遍顺便观察node1上的redis监视状态,并且时刻关注本机catalina.out日志变化是否异常。
#如下是正常情况下redis监视的状态输出
[root@tnode1 src]# /usr/local/redis/bin/redis-cli -p 6379 monitor
1556385116.932343 [0 192.168.111.4:37948] "EXPIRE" "DCF23D098140E899E20A996990F690D5" "1800"
1556385116.970109 [0 192.168.111.4:37948] "GET" "DCF23D098140E899E20A996990F690D5"
1556385116.972760 [0 192.168.111.4:37948] "EXPIRE" "DCF23D098140E899E20A996990F690D5" "1800"
1556385117.582753 [0 192.168.111.4:37948] "GET" "DCF23D098140E899E20A996990F690D5"
1556385117.584391 [0 192.168.111.4:37948] "EXPIRE" "DCF23D098140E899E20A996990F690D5" "1800"
1556385117.599639 [0 192.168.111.4:37948] "GET" "DCF23D098140E899E20A996990F690D5"
1556385117.600743 [0 192.168.111.4:37948] "EXPIRE" "DCF23D098140E899E20A996990F690D5" "1800"
1556385125.008432 [0 192.168.111.4:37948] "PING"
1556385155.006175 [0 192.168.111.4:37948] "PING"
1556386097.450914 [0 192.168.111.5:34118] "SETNX" "F7379EF99F21FD0BBF830056FEF162A0" "null"
1556386097.575639 [0 192.168.111.5:34118] "SET" "F7379EF99F21FD0BBF830056FEF162A0" "\xac\xed\x00\x05sr\x00Dcom.orangefunction.tomcat.redissessions.S
essionSerializationMetadataB\xd9\xd9\xf7v\xa2\xdbL\x03\x00\x01[\x00\x15sessionAttributesHasht\x00\x02[Bxpw\x14\x00\x00\x00\x10\x1f\xa2\xa9o\x15\x7f\xe1W\x9c\x9c\xc6\xc2\xb0\xd5\xe2\xa8xsr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01j3\x83F\xddsq\x00~\x00\x03\x00\x00\x01j3\x83F\xddsr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexq\x00~\x00\x04\x00\x00\a\bsr\x00\x11java.lang.Boolean\xcd r\x80\xd5\x9c\xfa\xee\x02\x00\x01Z\x00\x05valuexp\x01q\x00~\x00\nsq\x00~\x00\x03\x00\x00\x01j3\x83F\xddt\x00 F7379EF99F21FD0BBF830056FEF162A0sq\x00~\x00\a\x00\x00\x00\x00w\b\x00\x00\x01j3\x83F\xdd"1556386097.577377 [0 192.168.111.5:34118] "EXPIRE" "F7379EF99F21FD0BBF830056FEF162A0" "1800"
1556386097.586858 [0 192.168.111.5:34118] "EXPIRE" "F7379EF99F21FD0BBF830056FEF162A0" "1800"
1556386099.395455 [0 192.168.111.5:34118] "GET" "F7379EF99F21FD0BBF830056FEF162A0"
1556386099.403983 [0 192.168.111.5:34118] "EXPIRE" "F7379EF99F21FD0BBF830056FEF162A0" "1800"
1556386099.410867 [0 192.168.111.5:34118] "GET" "F7379EF99F21FD0BBF830056FEF162A0"
1556386099.413166 [0 192.168.111.5:34118] "EXPIRE" "F7379EF99F21FD0BBF830056FEF162A0" "1800"
1556386099.855877 [0 192.168.111.5:34118] "GET" "F7379EF99F21FD0BBF830056FEF162A0"
1556386099.857305 [0 192.168.111.5:34118] "EXPIRE" "F7379EF99F21FD0BBF830056FEF162A0" "1800"
1556386100.497947 [0 192.168.111.5:34118] "GET" "F7379EF99F21FD0BBF830056FEF162A0"
客户端访问http://192.168.111.3/session.jsp,是Nginx的地址来进行测试,正常时是sessionID不会变的
五、memcached和redis对比表
、 | 内存利用率 | 性能 | 数据持久化 | 高可用 |
---|---|---|---|---|
redis | 键值对村存储利用率低于memcached,但使用hash结构存储则超过后者 | 只使用单核CPU,数据大小100K以下快于后者 | 支持数据持久化(保存到硬盘) | Redis 支持数据的备份,即 master-slave 模式的数据备份,支持多种数据结构的存储 |
memcached | 如上 | 可以使用多核,100K以上快于前者 | 自身不支持持久化,但可以结合其他数据库做架构如:memcached + bdb | 服务器之间不支持同步数据,可以通过第三方工具进行分布式写入(magent) |
Tomcat之session解决方案的更多相关文章
- Apache shiro集群实现 (六)分布式集群系统下的高可用session解决方案---Session共享
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- Apache shiro集群实现 (五)分布式集群系统下的高可用session解决方案
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- 负载均衡集群中的session解决方案【转】
通常面临的问题 从用户端来解释,就是当一个用户第一次访问被负载均衡代理到后端服务器A并登录后,服务器A上保留了用户的登录信息:当用户再次发送请求时, 根据负载均衡策略可能被代理到后端不同的服务器,例如 ...
- 配置tomcat的session共享
可通过下面方法限制一个用户访问一个服务器之后就只在该服务器上操作. 请求负载过程中会话信息不能丢失.那么在多个tomcat中session需要共享. 配置tomcat的session共享可以有三种解决 ...
- 负载均衡集群中的session解决方案
前言 在我们给Web站点使用负载均衡之后,必须面临的一个重要问题就是Session的处理办法,无论是PHP.Python.Ruby还是Java,只要使用服务器保存Session,在做负载均衡时都需要考 ...
- 集群-Session解决方案
在集群中session安全和同步是个最大的问题,下面是收集到的几种session同步的方案,希望能通过分析其各自的优劣找出其适应的场景. 1. 客户端cookie加密 简单,高效.比较好的方法是自己采 ...
- tomcat中session在两个webapp中实现共享
现在遇到一个需求就是要求完成简单的单点登录,通过在一个tomcat实例中放置两个webapps应用ROOT应用和CEO应用来完成在ROOT应用登录后,在CEO可以直接使用,而未在ROOT应用登录时,不 ...
- 细说tomcat之session持久化探秘
业务场景:通常,我们会在会话级别存放一些参数,期望在session生命周期内,可以一直取得保存在session中的指定数据:而只要session过期或者失效,则需要执行重新登录等操作.但是!我们对于这 ...
- 针对负载均衡集群中的session解决方案的总结
在日常运维工作中,当给Web站点使用负载均衡之后,必须面临的一个重要问题就是Session的处理办法,无论是PHP.Python.Ruby还是Java语言环境,只要使用服务器保存Session,在做负 ...
随机推荐
- ubuntu 16.04 sudo nopasswd
修改sudoers配置文件:~$ sudo vi /etc/sudoers 在文件末尾添加需要sudo输入密码的user:[user] ALL=NOPASSWD:ALL 表示sysadmin这个组在进 ...
- React Native设置图片全屏背景显示
显示效果如下: 实现和页面代码如下: 1> 使用的react-navigation隐藏导航栏: AddTopic: {screen: AddTopicScreen,navigationOptio ...
- redcon, Redis兼容的服务器框架
源代码名称:redcon 源代码网址:http://www.github.com/tidwall/redcon redcon源代码文档 redcon源代码下载 Git URL: 复制代码 git:// ...
- super的实例及实现原理
super实例 class A(): def go(self): print ("go A go!") def stop(self): print ("stop A st ...
- C语言各数据类型大小和取值范围
- 使用Cython时遇到的cl.exe的问题
最近使用cython时,遇到一个问题,报错如下: cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD "-Id:\program files\python3 ...
- python 列表List - python基础入门(13)
列表是python开发过程中最常用的数据类型之一,列表俗称:list ,特点如下: 1.列表由一个或者多个数据构成,数据的类型可以不相同也可以相同: 2.列表中的数据需要写在[]中括号内部,数据与数据 ...
- 在airflow的BashOperator中执行docker容器中的脚本容易忽略的问题
dag模板 from airflow import DAG from airflow.operators.bash_operator import BashOperator from airflow. ...
- java源码 --List、Set、Collection
List和Set都是接口,它们继承与Collection.List是有序的队列,可以用重复的元素:而Set是数学概念中的集合,不能有重复的元素.List和Set都有它们各自的实现类. 为了方便,我们抽 ...
- ORACLE的客户端、后台进程
Oracle数据库进程之服务器进程详解 Oracle实例主要有三类进程:服务器进程.后台进程和从属进程.这里介绍一下Oracle数据库服务器进程的相关知识 Oracle数据库中的各个进程要完成某个特定 ...