session原理
- 用户使用浏览器第一次登录服务器成功后,服务器会创建 session 保存用户相关信息
- 接着服务器命令浏览器保存一个 jsessionid(作用域默认为当前访问服务器的域名),该jsessionid用于之后访问服务器时,拿到保存在 session 中的相关信息
- 再次访问服务器时,携带jsessionid,无需登录。因为根据jsessionid可以取到相关 session 信息
- 如果关闭浏览器,并清除 cookie(jsessionid保存再cookie中),再次向服务器发起访问时,这时候由于没有 jsessionid ,需要转到步骤1,重写发起登录。
分布式环境下,session 不共享问题
- 同域名下 session 不能共享问题
- 不同域名下,session 不能共享问题
分布式环境下,解决 session 共享问题
同域名下 session 共享问题解决
- 方案1,session 复制
优点:tomcat 原生支持 session复制,只需要修改配置文件即可
缺点:- session同步需要数据传输,占用大量网络带宽,降低服务器的业务处理能力
- 需要较多内存,无法水平扩展更多的 web-server。因为每台 web-server 都需要存储所有 session数据
- 方案2,客户存储 session (这种方式不会使用,提供一种思路)
优点:服务器不需要存储 session,用户保存自己的 session 信息到 cookie 中,节省服务器资源
缺点:- 每次 http 请求都需要携带 cookie中的完整信息,浪费网络带宽
- cookie长度有 4k限制,所有 session不能太长
- session 数据存放在cookie中,存在泄漏,篡改,窃取等安全隐患
- 方案3,一致性hash
优点:
- 只需要配置 nginx,不需要修改代码
- 只要被hash的字段字分布均匀,那么每台web-server的负载就是均衡的
- 可以支持web-server水平扩展
缺点:
- session 存在web-server 中,web-server 重启导致部分 session 丢失,影响业务。如部分用户需要重写登录
- web-server水平扩展后,rehash后session重写分布,也会有部分用户路由不到正确的 session,因此需要重写登录
这些缺点的问题不大,因为session本来就是有有效期的。因此这种反向代理方式可以使用。
- 方案4, 统一存储到DB或者Redis中
优点:
- 没有安全隐患
- 可以水平扩展
- web-server重启或者扩容,都不会有session丢失
缺点:
- 增加了一次网络调用,需要修改业务代码,如将所有的 getSession 方法替换为从Redis中查询数据
- 从 redis 获取数据比从内存中慢
上面的缺点,可以用 SpringSession解决
不同域名下,session 共享问题
- 访问商品服务完成后,服务器命令浏览器保存 jsessionid 到 mall.com 域名下,(默认保存会保存在product.mall.com域名下,服务可以将jsessionid的作用域放大到父级域名 mall.com)
- 访问订单服务,此时会携带jsessionid,因为 order.mall.com 域名为 mall.com 域名的子域名,而jsessionid保存在 mall.com域名下,因此,访问 mall.com下的所有子域名,都会携带jsessionid。
如此,解决不同子域名下,session共享问题。
分布式情况下,使用 SpringSession 实现同域名和不同子域名 session 共享
参考 https://docs.spring.io/spring-session/docs/2.5.1/reference/html5/#httpsession-redis
- 添加依赖
<dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
- application.yml文件中配置
spring: session: store: type: redis # 调整 session 的过期时间 server: servlet: session: timeout: 30m
- Spring Boot 帮我自动创建了 springSessionRepositoryFilter
Our Spring Boot Configuration created a Spring bean named springSessionRepositoryFilter that implements Filter. The springSessionRepositoryFilter bean is responsible for replacing the HttpSession with a custom implementation that is backed by Spring Session.
- 添加注解 @EnableRedisHttpSession 确保 SpringSession 来管理 HttpSession
- 自定义cookie,修改 session的作用域
/** * 该类放入两个bean到spring容器中 * 1. 主要解决session作用域问题 * 2. 解决session存储到redis中,修改默认的存储方式 */ @Configuration public class MallSessionConfig { /** * 自定义cookie,因为cookie是存储在客户端的。 * 为了解决session子域共享问题,我们自定义存储cookie的时候, * 修改cookie的默认作用域,将cookie的作用域放大到父级别的域名 * * @return */ @Bean public CookieSerializer cookieSerializer() { DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer(); defaultCookieSerializer.setDomainName("mall.com"); defaultCookieSerializer.setCookieName("MALLSESSION"); return defaultCookieSerializer; } /** * 修改默认的序列化机制,默认采用jdk序列号机制, * 这里让存在redis中的数据为json数据,而不是 * 使用jdk序列化后的数据 * @return */ @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer() { return new GenericFastJsonRedisSerializer(); } }