分布式环境session解决的4个方案
方案1:session复制(session同步)
原理:就是让这两个服务器之间互相同步session,比如左边服务器之前保存了一个1,右边服务器之前保存了一个2,他们两个一同步,那么左边服务器保存了1,2,右边服务器也保存了1,2。这样做的话,我们无论去哪个服务器,都相当于能拿到全量的session数据,这样就不用担心负载均衡到哪个服务器了
优点:tomcat原生支持,只需要修改一下配置文件,好多tomcat之间就能复制session
缺点:
- session同步需要通过网络进行数据传输,就有延迟问题,同时会占用大量带宽,这样会压缩我们整个业务的带宽,会降低我们的处理能力
-
假如我们这里有100台tomcat,每一个tomcat里面session都只存了1G的数据,我们想要用同步方案来做的话,那相当于其他tomcat都要保存剩下99个人的所有全量数据,那相当于每个tomcat都至少需要100G的内存才能将session整个全量保存下来。因此,这个解决方案受到内存限制,我们服务器无法水平扩展,不能复制上好多个tomcat来进行使用
总结:如果是大型分布式集群环境,由于所有的web-server都全量保存数据,所以这种方案我们不使用。而如果是小型系统里面,就3/5个tomcat,我们想使用的话,就简单配置一下也还可以
方案2:客户端存储
原理:我们让客户端自己来存储session,我们服务器想用哪些数据,读取浏览器带过来的cookie即可。这样可以节省服务器资源
缺点:都是缺点
- 每次http请求,携带用户在cookie中的完整信息,浪费网络带宽
- session数据放在浏览器的cookie中,有些浏览器遵循的标准不一样,它的长度限制不一样,比如长度限制4k,因此不能保存大量信息
- session数据放在cookie中,存在泄露、篡改、窃取等安全隐患
总结:上面的缺点都很致命,因此我们实际绝对不会使用这种方案
方案3:HASH一致性(推荐)
原理:利用了我们负载均衡机制,我们可以利用ip的哈希一致性,只要来自于同一个ip的,那我们就永远给它定位到同一个服务器,我们也不给它跑到第二个服务器了,这样比如标绿色的浏览器去的标绿色的服务器里面存的东西,无论多少次请求过来,都会落到标绿色服务器的身上,我们就能取得到
我们hash一致性还可以结合业务字段,如下面的图,凡是456号用户他的请求都落在绿色服务器,凡是123号这个用户他的请求都落在橙色服务器
优点:
- 只需要改负载均衡nginx的配置,让它做一个ip hash,而不需要修改应用代码
- 负载是均衡的:只要hash属性的值分布是均匀的,多台web-server的负载就是均衡的
- 支持web-server水平扩展(而session复制方案是不行的,受内存限制)
缺点:
- session还是存在web-server中,因此web-server突然闪断或者重启了,可能会导致部分session丢失,这部分用户只要下次再过来,所有的数据都没了,他需要重新登录一遍,所有东西都得重新做一遍
-
如果我们服务器要水平扩展,如果我们固定了也好,但是如果原来是2台服务器,现在加到了4台服务器,现在想要做哈希的话,相当于重新得计算一下,假设我们以前计算哈希最简单的方式,按照ip地址得到一个整数型的哈希,如果只有2台服务器,那么就可以对2求余操作,求到余数,如果余数是1,就落到第一台服务器,如果没有余数,就落到第二台服务器。但如果变成了4台服务器,我们相当于就要对4进行求余操作,如果余1,落到第一台服务器,余2落到第二台服务器,余3落到第三台服务器,没有余数我们落到第四台服务器。(即水平扩展后,rehash后session重新分布,会有一部分用户路由不到正确的服务器)
总结:以上缺点问题不大,而且后来呢,我们ip 哈希的这种也用的比较多,因为基于session本来就是具有有效期的,就算这次因为水平扩展原因或者服务器闪断原因没有了,那就相当于浏览器关掉了呗,那我们让用户重新再做一次登录即可
方案4:统一存储(推荐)
原理:
我们之前出现的所有问题是,因为浏览器访问我们服务的时候,由于负载均衡机制会跳到不同的服务器,而又由于session是每个服务器各自存储在各自内存空间的,所以这导致我们跳到下一个服务器以后,我们上一个服务器session里面的数据它就用不到了,那怎么办呢?
那我们就可以让session统一存储,无论是你哪个服务器,哪个tomcat,你的session都不要存储到你的内存里面了,全部呢,大家都可以存到数据库,或者redis之类的速度更快的nosql中间件等等,所以,我们可以使用这种方案
优点:
-
没有安全隐患,没有让浏览器自己存储到cookie里,所有的数据都是我们后台统一存储,浏览器肯定是没办法访问到的,只要我们保障了我们后台的redis的安全,就没有人能去篡改里面相关的数据
-
水平扩展也很容易,无论我们web服务器有多少个,10个,100个,1000个,反正大家都去redis中做存取,即使redis不够用了,我们做redis集群,每个里面存一点,每个里面存一点
-
括我们服务器即使重启、宕机,下次再启动了,我们session也不会丢失,因为session都是redis里面存着,跟我们业务服务器宕机与否没有任何关系
缺点:
-
从内存中取数据是非常快的,也不需要网络交互,而如果我们存储到了redis里面,我们想要从session里面取数据,我们还得连接redis,再来一次网络交互
-
我们需要修改应用代码:如将所有的getSession方法替换为从Redis查数据的方式
总结:好的一点是,spring早就意识到了这个问题,专门编写了一个框架叫SpringSession,它就可以完美的解决我们session统一存储问题
session共享问题之不同服务-子域session共享
什么叫子域:比如之前发给auth.mall.com,我们可以给它放大作用域,比如我们只要是mall.com域名下的,我们都能用
因此如果在登录成功往浏览器回写cookie的时候,把这个作用域域名能改成父域名就好了,但这个操作又是我们tomcat默认自己发的,你第一次使用session,他就自己发的,所以如果我们自己来写这段逻辑还是非常麻烦的,我们整合springSession来解决这个问题
流程:
首先浏览器会在认证服务里面登录成功,认证服务会将登录成功的用户存储到session里面,但是它存session的时候,我们不让他存储到自己的内存里面,我们让它存储到redis里面,接下来呢,给我们浏览器回写cookie里面是jsessionid,回写的时候,又让我们这个session对应的jsessionid这个cookie默认的作用域不能只是我们认证服务,让它放大作用域,一放大到mall.com以后,我们浏览器下次访问任何服务,那我们都会带上这个cookie叫jsessionid的,因为我们访问其他服务都是其他子域名的,因为所有服务,session都统一用redis存取,所以我们访问到了其他服务,其他服务就算要按照jsessionid取出session里面的数据,他也去session里面查