常见的会话解决方案
- 粘性会话 Sticky Session
让一个用户的会话,尽可能将它的请求坐落在同一台机子上面,把分布式Session变成一个单机的Session
问题是如果我的服务器发生变故,比如说,服务器下线,这样的话,原先坐落这台上的请求就会分配到别的机子上,这个时候,原先这个用户持有的会话,就不生效了,用户体验不好 - 会话复制 Session Replication
把每台机子上的会话,都做一个复制,这样的话,我这个集群里面的每台机器都持有几乎相同的会话信息,不管请求落到哪台机子上,都可以取得session
问题是:session的复制是有成本的,而且如果访问量巨大,每台机子持有如此大的会话,是不现实的,另外,每台机子上的副本信息有可能是不一样 - 集中会话 Centralized Session
使用 jdbc或者redis 这样的设施集中存储会话信息,只要有相同的sessionid,我们就可以把这些session从集中的会话中取出来,不管请求是在哪台机子上面,只要持有相同的sessionid,那,我就可以保证,它可以取得相同的会话。
认识 Spring Session
Spring Session (与这些容器无关)
- 简化集群中的用户会话管理
- 无需绑定容器特定解决方案
支持的存储
- Redis
- MongoDB
- JDBC
- Hazelcast
实现原理
定制 HttpSession
- 通过定制的 HttpServletRequest 返回定制的 HttpSession(这个session可以屏蔽掉后端的各种各样的差异,不管是使用jdbc 还是 redis 都以一样的接口 向外暴露)
•SessionRepositoryRequestWrapper
•SessionRepositoryFilter
•DelegatingFilterProxy
这三个过滤器 是主要类
基于 Redis 的 HttpSession
引入依赖
- spring-session-data-redis
基本配置
- 加入@EnableRedisHttpSession注解
- 提供 RedisConnectionFactory配置
- 实现 AbstractHttpSessionApplicationInitializer
• 配置 DelegatingFilterProxy
Spring Boot 对 Spring Session 的支持
在application.properties做一些属性的设置就可以代替上面的操作
- spring.session.store-type=redis
- spring.session.timeout=
• server.servlet.session.timeout= - spring.session.redis.flush-mode=on-save
- spring.session.redis.namespace=spring:session
例子
在docker中登录redis命令
docker start redis 启动redis
docker exec -it redis bash 打开 redis
redis-cli -p 6379 输入密码
进入后 使用flushdb 删除 当前的所有数据
也 可以 用 del 一个个删除
@SpringBootApplication
@RestController//如果控制器产生的结果希望让人看到,那么它产生的模型数据需要渲染到视图中,从而可以展示到浏览器中,使用@Controller。
// 如果控制器产生的结果不需要让人看到,那么它产生的数据经过消息转换器直接返回到浏览器,使用@RestController。
@EnableRedisHttpSession //开启 Redis 的 HttpSession服务
public class SessionDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SessionDemoApplication.class, args);
}
@RequestMapping("/hello") //从request当中取得一个session 看session当中 做了一个name的设置 如果有就取出来使用 如果没有 就有hello后面带的name做一个赋值 直接返回一个hello "hello " + storedName;
public String printSession(HttpSession session, String name) {
String storedName = (String) session.getAttribute("name");
if (storedName == null) {
session.setAttribute("name", name);
storedName = name;
}
return "hello " + storedName;
}
}
运行之后 打开谷歌 输入http://localhost:8080/hello?name=spring
结果如下:
打开 reids
使用 keys * 查找出所有的key
使用 type 查看他们的类型
使用HGETALL 读取内容
可以看到 hash中 记录了session的创建时间,修改时间等等
这些 都是 springdataredis 帮我们做的一个序列化 把它整个保存在redis中
如果将地址改为http://localhost:8080/hello?name=sp
结果不变,如果是单机会话,如果我的进程没有了,我的会话信息也就没有了。现在,我们重新启动程序,重新启动之后,服务器进程重启,但是会话信息保存在redis中,只要使用相同的sessionid,我们还是能取得那个session。使用基于redis的集中会话。