共计 3497 个字符,预计需要花费 9 分钟才能阅读完成。
我发现了一个商城,我还没有登录,就能够往购物车中增加商品,加了好几件后,我筹备付款,须要我先去登录,登录完之后付款。
当初很多商城,都会要求用户先去登录,登录之后再往购物车中增加商品,这样用户、购物车、商品,三个对象之间就有了绑定关系。
而针对我最开始说的那种状况,其实就是基于 session
做的,客户端往购物车中增加第一个商品的时候,发送一个申请,服务器收到申请之后,创立 session
,而后返回以后session
对应的一个 JessionId
,浏览器存储在cookie
中,客户端往购物车增加第二个商品时,携带 JessionId
,服务端收到申请后,更新session
。浏览器敞开后,cookie
生效,JessionId
也就失落了,须要从新往购物车中增加商品,默认状况下,session
有效期为 30
分钟。
在分布式环境下,session
就会呈现问题了,如果服务端部署在两个服务器 A
和B
上。第一次往购物车增加商品时,申请落在了服务器 A 上,服务器 A 创立了一个 session
,并返回JessionId
,第二次往购物车增加商品时,申请落在了服务器 B 上,申请携带的JesssionId
在服务器 B 上并不会找到对应的session
。这时候服务器 B 就会创立一个新的session
,并返回对应的JessionId
,客户端发现第一次增加的商品失落了。。。
接下来,一起来学习分布式环境下 session
一致性是如何实现的。
一、客户端存储
既然分布式环境中,一个客户端的多个申请可能会落在多个服务器上,那么咱们是否能够扭转策略,间接将 session 信息存储在客户端?能够的,服务器将 session 信息间接存储到 cookie 中,这样就保障了 session 的一致性,然而并不举荐这样去做,因为将一些信息存储在 cookie 中,相当于就把这些信息裸露给了客户端,存在重大的安全隐患。
毛病:
- 安全性存在问题
- cookie 对于数据类型及数据大小有所限度
二、session 复制
将服务器 A 的 session,复制到服务器 B,同样将服务器 B 的 session 也复制到服务器 A,这样两台服务器的 session 就统一了。像 tomcat 等 web 容器都反对 session 复制的性能,在 同一个局域网内 ,一台服务器的session
会播送给其余服务器。
毛病:
同一个网段内服务器太多,每个服务器都会去复制 session,会造成服务器内存节约。
三、session 黏性
利用 Nginx
服务器的反向代理,将服务器 A 和服务器 B 进行代理,而后采纳 ip_hash
的负载策略,将客户端和服务器进行绑定,也就是说客户端 A 第一次拜访的是服务器 B,那么第二次拜访也必然是服务器 B,这样就不存在 session 不统一的问题了。
毛病:
如果服务器 A 宕机了,那么客户端 A 和客户端 B 的 session 就会呈现失落。
四、session 集中管理
这种形式就是将所有服务器的 session
进行对立治理,能够应用 redis
等高性能服务器来集中管理 session,而且 spring 官网提供的 spirng-session
就是这样解决 session
的一致性问题。这也是目前企业开发用到的比拟多的一种分布式 session
解决方案。
五、spring-session 实战
Spring
提供了解决分布式 session 的解决方案——Spring Session
。Spring Session
提供了用于治理用户会话的 API 和实现。
Spring Session
提供了对 redis
,mongodb
,mysql
等罕用的存储库的反对,Spring Session
提供与 HttpSession
的通明整合,这意味着开发人员能够应用 Spring Session 反对的实现切换 HttpSession
实现。还是原来的配方,产生了不一样的滋味!
Spring Session
增加了一个 SessionRepositoryFilter
的过滤器,用来批改包装申请和响应,包装后的申请为 SessionRepositoryRequestWrapper
,调用getSession()
办法的时候实际上就是调用 Spring Session
实现了的 session。
Spring Session
应用非常简单,增加了相干依赖后,间接操作 HttpSession
就能够实现成果。
第一步 :增加Spring Session
和 redis
的相干依赖
`<dependency>`
`<groupId>org.springframework.boot</groupId>`
`<artifactId>spring-boot-starter-web</artifactId>`
`</dependency>`
`<dependency>`
`<groupId>org.springframework.session</groupId>`
`<artifactId>spring-session-data-redis</artifactId>`
`</dependency>`
`<dependency>`
`<groupId>org.springframework.boot</groupId>`
`<artifactId>spring-boot-starter-data-redis</artifactId>`
`</dependency>`
`<dependency>`
`<groupId>org.apache.commons</groupId>`
`<artifactId>commons-pool2</artifactId>`
`</dependency>`
第二步:配置 redis 相干信息
`spring:`
`redis:`
`# redis 库 `
`database: 0`
`# redis 服务器地址 `
`host: localhost`
`# redis 端口号 `
`port: 6379`
`# redis 明码 `
`password:`
`# session 应用 redis 存储 `
`session:`
`store-type: redis`
第三步:我的项目中应用 session
`public String sessionTest(HttpServletRequest request){`
`HttpSession session = request.getSession();`
`session.setAttribute("key","value");`
`return session.getAttribute("key").toString();`
`}`
redis
中每个 session 存储了三条信息。
- 第一个存储这个 Session 的 id,是一个 Set 类型的 Redis 数据结构。这个 k 中的最初的 1439245080000 值是一个工夫戳,依据这个 Session 过期时刻滚动至下一分钟而计算得出。
- 第二个用来存储 Session 的详细信息,包含 Session 的过期工夫距离、最近的拜访工夫、attributes 等等。这个 k 的过期工夫为 Session 的最大过期工夫 + 5 分钟。如果默认的最大过期工夫为 30 分钟,则这个 k 的过期工夫为 35 分钟。
- 第三个用来示意 Session 在 Redis 中的过期,这个 k - v 不存储任何有用数据,只是示意 Session 过期而设置。这个 k 在 Redis 中的过期工夫即为 Session 的过期工夫距离。
解决一个 session 为什么要存储三条数据,而不是一条呢!对于 session 的实现,须要监听它的创立、过期等事件,redis 能够监听某个 key 的变动,当 key 发生变化时,能够疾速做出相应的解决。
然而 Redis 中带有过期的 key 有两种形式:
- 当拜访时发现其过期
- Redis 后盾逐渐查找过期键
当拜访时发现其过期,会产生过期事件,然而无奈保障 key 的过期工夫到达后立刻生成过期事件。
spring-session 为了可能及时的产生 Session 的过期时的过期事件,所以减少了:
spring:session:sessions:expires:726de8fc-c045-481a-986d-f7c4c5851a67
`spring:session:expirations:1620393360000`
spring-session 中有个定时工作,每个整分钟都会查问相应的 spring:session:expirations: 整分钟的工夫戳 中的过期 SessionId,而后再拜访一次这个 SessionId,即spring:session:sessions:expires:SessionId,以便可能让 Redis 及时的产生 key 过期事件——即 Session 过期事件。