由于HTTP协议是无状态的协议,一次浏览器和服务器的交互过程就是:
浏览器:你好吗?
服务器:很好!
这就是一次会话,对话完成后,这次会话就结束了,服务器端并不能记住这个人,下次再对话时,服务器端并不知道是上一次的这个人,所以服务端需要记录用户的状态时,就需要用某种机制来识别具体的用户,这个机制就是Session。
浏览器中的Cookie也是K-V结构,key放的是SessionId ,value放的是具体的值。
下图是sessionId存放redis中的数据
HASH是sessionId
sessionAttr:session属性
maxInactiveInterval:最大存在时间,默认是30分钟,单位是秒
lastAccessedTime:最后一次访问时间,单位是毫秒,每次sessionId返回这个值是一定更新的,所以其他值也会跟着更新
creationTime:sessionId创建时间,单位是毫秒
这个session是加了定时器的,到30分钟自动销毁
nginx多服务器下浏览器访问,会出现session丢失。
实际上通过上面介绍的单服务器对sessionId的识别与创建可以知道,如果Tomcat服务器的session容器中找不到sessionId的值,那就会判定是第一次访问,主动给它生成sessionId。
在多个服务器下,通过nginx的负载均衡,出现可以访问多个服务器的情况。访问第一个服务器,由第一个服务器给sessionId赋值,返回给客户端浏览器,之后再次发出请求,这个请求分配给了另一个Tomcat服务器,这个服务器的Session容器中没找到传过来的sessionId的值,这时它认为这时第一次过来访问的,就给sessionId赋一个新值,响应结果后把sessionId返回给浏览器。之后在发送请求,这个请求分配给第一次访问的Tomcat,它找不到sessionId的值,判定第一次访问,又给重新赋值…
(sessionId是通过UUID生成,全球唯一,不存在重复的可能性)
这就是session丢失的原因。
比如基于Tomcat的tomcat-redis-session-manager插件,基于Jetty的jetty-session-redis插件、memcached-session-manager插件
优点:对项目来说是透明的,无需改动代码
缺点:由于过于依赖容器,一旦容器升级或者更换意味着又得重新配置(其实底层是,复制session到其它服务器,所以会有一定的延迟,也不能部署太多的服务器。)
用户每次访问都绑定到同一台具体的后台tomcat服务器实现session总是存在
在需要使用会话的时候都从自己的工具类中获取,而工具类后端存储可以放到Redis中
优点:灵活性很好,自己写
缺点:不容易写,开发需要一些额外的时间,不一定稳定
这个方案既不依赖tomcat容器,又不需要改动代码,只需修改对应的配置文件,由Spring session框架为我们提供,可以说是目前非常完美的session共享解决方案
将存放session的Session容器放在一个公共的位置,让每一个服务器都可以访问到,像是redis,数据库,磁盘文件等地方
<dependency>
<groupId>org.springframework.sessiongroupId>
<artifactId>spring-session-data-redisartifactId>
<version>1.3.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>4.3.16.RELEASEversion>
dependency>
<filter>
<filter-name>springSessionRepositoryFilterfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
包扫描配置,这个标签中包含了
的全部功能
<context:annotation-config/>
<bean id="redisHttpSessionConfiguration" class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
<property name="cookieSerializer" ref="defaultCookieSerializer"/>
bean>
<bean id="defaultCookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
<property name="cookiePath" value="/"/>
bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="192.168.245.128"/>
<property name="port" value="6379"/>
<property name="password" value="123456"/>
bean>
<import resource="applicationContext-session.xml"/>