分布式环境中如何解决 Session 的问题

常见的会话解决方案

  • 粘性会话 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的集中会话。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值