分布式系统下的Session使用
单体项目下Session的使用
用户通过浏览器访问服务器时,后台会根据当前的用户信息创建一个对应的session并保存在服务器的内存中,同时会通过一个JSESSIONID
的cookie保存在用户的浏览器的cookie storage中,以后每次发送请求都会携带上它。
服务器就可以根据这个JSESSIONID
来辨别当前是那个用户的会话
Tomcat的底层中,它是通过map的形式来进行session
的保存
其中JSESSIONID
对应cookie的domain
是当前访问的请求的domain
迁移到分布式系统中存在的问题
问题一:
session
只是保存在每台服务器的内存中,分布式项目中不同的服务部署在不同的机器上,那么session
一定无法进行同步共享,因而如果采用轮询的负载均衡方式进行服务的请求将会导致session不共享而使得用户可能在当前会话期间重复的进行登录
问题二:
二级域名auth.gullmall.com
下进行session保存到对应的cookie
的domian
是二级域名,那么如果从auth.guilmall.com
跳转一级域名gulimall.com
是无法获取到JSESSIONID
的cookie
对应的解决方式
问题一的解决方式:
- 通过
tomcat
的底层配置,修改配置文件,让Session的信息在tomcat之间的复制。但是这个非常的不推荐,因为tomcat的数量越多则当前tomcat需要复制其他tomcat容器的session内容越大,这样在内存方面是不可能实现的 - 通过
客户端
包含会话信息的,这种是将当前用户与网站交互所需要的信息都包含在cookie中,每次进行请求的时候都携带上这些内容。这种方式也是几乎不可能实现的,因为请求头中header是存在4kb的限制的,超过这个限制则无法进行请求的发送,其次这也会带来安全问题,如果用户遭到浏览器的安全问题而导致这些cookie信息的暴露将会泄露用户的信息,进一步的将会导致用户在网站中资源的被盗取 - 通过
ip_hash
的方式,而不通过轮询的方式来,这个通过固定用户请求的服务器保证当前用户的session是保存在当前的服务器中。但是这样的可扩展性差,如果服务的机器数量进行扩容,则还需要重新ip_hash的计算,此外如果访问的途中,服务器进行重启或者其他将会导致session的丢失 - 第四种则是通过统一的session保存机制,也即session的存储可能是在MYSQL中或者redis中,不同的服务如果需要进行
session
的使用则从统一的session管理处获取即可,这样就进行session的统一管理。但是这样也存在问题,需要进行另外的配置,通过自定义的方式进行实现也非常的麻烦,但是很好的是,spring session
可以来帮助我们完成这样的功能
问题二的解决方式:
原生的session进行JSESSIONID的存储的domain
是使用默认的,而我们只需要在进行创建的时候进行修改即可,但是手动修改是非常的麻烦。但是恰好是,spring session
提供我们自己配置的方式来修改domain
来保证JSESSIONID
的存储是通过一级域名的方式进行存储(cookie的domain域的自动访问是通过浏览器来进行的)
SpringSession的解决方案
介绍:是什么
Spring Session provides an API and implementations for managing a user’s session information.
特性:
Spring Session makes it trivial to support clustered sessions without being tied to an application container specific solution.
Spring Session使得可以进行集群的session支持而不去绑定指定的容器的解决方案
它还提供事务集成 transaction integration
:
HttpSession
- allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way, with support for providing session IDs in headers to work with RESTful APIs(即:自定义HttpSession来进行session的管理)WebSocket
- provides the ability to keep the HttpSession alive when receiving WebSocket messagesWebSession
- allows replacing the Spring WebFlux’s WebSession in an application container neutral way
使用Spring Session的三种方式
:
- Spring Session JDBC
- Spring Session Data Redis
- Spring Session Hazelcast
第一步:导入配置
<!--spring session data redis:使用redis作为session方式-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!--对应则需要进入redis的启动包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
第二步:编写配置
spring.session.store-type=redis # 指定redis作为session的存储方式
server.servlet.session.timeout= 指定session的过期时间,默认为秒
spring.session.redis.flush-mode=on_save # Sessions flush mode.
spring.session.redis.namespace=spring:session # 键的前缀名
第三步:1.自定义redis存储session的形式
//redis中存储session中的数据是通过jdk的序列化机制进行存储的,如果希望通过json的形式则需要注入RedisSerizlizer
@Bean
public RedisSerializer redisSerializer(){
return xxx;//可以使用fastjson的序列化机制进行
}
第三步:2.自定义JSESSIONID的存储(解决子域cookie不共享的问题)
//只需要注入CookieSerializer即可
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("JSESSIONID");
serializer.setCookiePath("/");
serializer.setDomain();//进行显式的将cookie的domain的作用域的修改
serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
return serializer;
}
第四步:开启spring session redis
@EnableRedisSpringSession
第五步:使用
HttpSession session
我们只需要正常的调用,session.setAttribute()即可,底层默认会进行session的转换
Spring Session的核心原理(老版)
主要就是通过
装饰器模式
,包装原来的session为spring session后续的filter执行链都将会获取到这个包装后的session,也即
request.getSession()
是经过重写的方法
RedisOperationSessionRepository
:使用redis操作session的CURD操作类
SessionRepositoryFilter
:存储过滤器,每个请求都必须经过的filter
说明:本文根据尚硅谷-谷粒商城来进行整理
est.getSession()`是经过重写的方法
RedisOperationSessionRepository
:使用redis操作session的CURD操作类
SessionRepositoryFilter
:存储过滤器,每个请求都必须经过的filter
说明:本文根据尚硅谷-谷粒商城来进行整理