集群应用Session一致性实现的三种方案
1. 为什么会有Session?
· HTTP 协议本身是无状态的,这与HTTP协议本来的目的是相符的,客户端只需要简单的向服务器请求下载某些文件,无论是客户端还是服务器都没有必要纪录彼此过去的行为,每一次请求之间都是独立的,好比一个顾客和一个自动售货机或者一个普通的(非会员制)大卖场之间的关系一样。
· 由于HTTP协议是无状态的,而出于种种考虑希望HTTP协议之间的通信是有状态的,
· 比如我希望记录客户的购买记录,有利于数据推送
· 登陆状态的记录,电影的播放记录等等
这些都需要记录相应的状态,是无状态变成有状态,为了记录状态出现了cookie和session
1.1. Session简单介绍
在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
1.2. Session和Cookie的主要区别
1.2.1. cookie官方解释:
Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)
1.2.2. session官网解释:
在计算机中,尤其是在网络应用中,称为“会话控制”。Session对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
综上Session和Cookie的主要区别如下:
A. Cookie是把用户的数据写给用户的浏览器。
B. Session技术把用户的数据写到用户独占的session中。
C. Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。
1.3. Session实现原理
服务器创建session出来后,会把session的id号,以cookie的形式回写给客户机,这样,只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机浏览器带session id过来了,就会使用内存中与之对应的session为之服务。
2. 为什么要保持Session一致性?
首先看标题,应用集群就说明并非单台服务器对外服务,而多个应用服务器(见下图)对外提供服务时,用户的一系列操作可能分散在不同的机器上完成,这时应用端的用户权限验证就会出现问题,因为session信息没有同步,权限验证会导致用户操作失败,所以应用集群环境下必然要考虑的一个问题就是保证session的一致性。
3. Session一致性问题的三种解决方案
注意:以下3种方案均需要nginx做负载均衡。
3.1. 方案一:Nginx ip_hash
每个用户请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。使用ip_hash配置时nginx负载均衡策略使用默认即可(轮询)。 注意ip_hash配置不能和权重(weight)配置同时使用。
缺点:当服务器挂掉后,无法对外提供服务。
3.1.1. 配置方式
nginx安装参照:http://www.runoob.com/linux/nginx-install-setup.html
nginx配置文件nginx.conf中添加如下配置
1) 集群配置
upstream sesiontest {
ip_hash;
server 192.168.11.115:8080;
server 192.168.11.120:8080;
}
2) 服务监听与代理配置
server {
listen 80;
server_name sessiontest;#集群配置名称
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
proxy_pass http://sessiontest; #集群配置名称
#url:http://ip:端口(默认80可省略)/目录下的请求挥别代理到集群的服务其中
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
3.2. 方案二:服务器Session复制
Tomcat集群组播共享Session。
缺点:tomcat集群之间组播通信消耗系统资源。如果系统遇到网络问题,会导致服务无法正常使用。
3.2.1. 配置方式
1) 修改Tomcat的server.xml配置文件。在<Engine name="Catalina" defaultHost="localhost">下面添加如下配置
略:
可参考:http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html
2) 修改应用配置文件web.xml
在应用的web.xml配置文件中添加<distributable/>标签
3.3. 方案三:Session集中统一管理
通过Redis或mongodb来存入和读取Session信息,集群应用共享,达到Session统一管理的目标。(spring+redis+spring-session整合)
redis安装参考:http://blog.csdn.net/u012661010/article/details/73776066
项目代码:https://github.com/JianJang2017/spring-redis-session.git
3.3.1. 配置方式
3.3.1.1. jar包依赖
<!-- Spring-Session+Redis实现session共享依赖 start-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<!-- Spring-Session+Redis实现session共享依赖 end-->
3.3.1.2. 整合配置
spring-redis-session.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--Spring-Session+Redis实现session共享 redis配置-->
<bean id="redisHttpSessionConfiguration"
class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="maxInactiveIntervalInSeconds" value="600" />
</bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="100" />
<property name="maxIdle" value="10" />
</bean>
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
destroy-method="destroy">
<property name="hostName" value="192.168.11.115" />
<property name="port" value="6379" />
<property name="password" value="" />
<property name="timeout" value="3000" />
<property name="usePool" value="true" />
<property name="poolConfig" ref="jedisPoolConfig" />
</bean>
</beans>
3.3.1.3. session管理配置
web.xml配置:必须位于filter链的最前面,可在encodingFilter后面
<!-- spring-session:必须位于filter链的最前面,可在encodingFilter后面 -->
<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3.3.1.4. 异常处理
1) 遇到的异常:
Caused by: redis.clients.jedis.exceptions.JedisDataException: MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk.
Commands that may modify the data set are disabled. Please check Redis logs for details about the error
2) 解决方法
略
参照:http://www.jianshu.com/p/3aaf21dd34d6
https://www.cnblogs.com/qq78292959/p/3994349.html
3.3.2. 测试
1) 在Redis的客户端redis-cli下通过命令keys * 查看session信息。
2) 通过hgetall查看hash信息