前言:
我发现了一个商城,我还没有登录,就可以往购物车中添加商品,加了好几件后,我准备付款,需要我先去登录,登录完之后付款。
现在很多商城,都会要求用户先去登录,登录之后再往购物车中添加商品,这样用户、购物车、商品,三个对象之间就有了绑定关系。
而针对我最开始说的那种情况,其实就是基于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的解决方案——SpringSession。SpringSession提供了用于管理用户会话的API和实现。
SpringSession提供了对redis,mongodb,mysql等常用的存储库的支持,SpringSession提供与HttpSession的透明整合,这意味着开发人员可以使用SpringSession支持的实现切换HttpSession实现。还是原来的配方,产生了不一样的味道!
SpringSession添加了一个SessionRepositoryFilter的过滤器,用来修改包装请求和响应,包装后的请求为SessionRepositoryRequestWrapper,调用getSession()方法的时候实际上就是调用SpringSession实现了的session。
SpringSession使用非常简单,添加了相关依赖后,直接操作HttpSession就可以实现效果。
第一步:添加SpringSession和redis的相关依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.session/groupIdartifactIdspring-session-data-redis/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.apache.