一口气说出 4 种分布式一致性 Session 实现方式,面试杠杠的~
Hollis共
4001字,需浏览
9分钟
·
2020-07-29 01:05
阿粉公司有一个 Web 管理系统,使用 Tomcat 进行部署。由于是后台管理系统,所有的网页都需要登录授权之后才能进行相应的操作。起初这个系统的用的人也不多,为了节省资源,这个系统仅仅只是单机部署。后来随着用的人越来越多,单机已经有点扛不住了,于是阿粉决定再部署了一台机器。这时后端系统有两台服务,于是我们使用 Nginx 作为反向代理,整体架构图如下:经过一些调试之后,在一个夜深人静的晚上,阿粉将这套系统部署到了生产。本以为没有什么事的,很稳的交给测试小姐姐开始测试。这一测,出了大问题!测试小姐姐反馈,登录过后,没过一会又需要登录,操作好几次都是这样。阿粉检查了一下,系统应用,配置什么也没问题,那到底哪里出了问题?这个时候组长刚准备下班,看到我们这里有问题,于是过来了看了一下。简单了解的一下基本情况,很快就找到了问题的原因,然后在 Nginx 端修改了下配置,重启解决了问题。
分布式一致性 Session
解决完问题,组长坐下给阿粉解释了问题原因:分布式一致性 Session。原先我们登录之后将会把用户登录信息放在 Session 中,用户每次操作首先先校验 Session 是否存在用户信息,如果不存在将会强制让用户先去登录。原先架构的中我们只有一台应用系统,所有操作都在一台 Tomcat 上,这当然没有什么问题。但是现在我们部署了两台系统,由于 Nginx 使用默认负载均衡策略(轮询),请求将会按照时间顺序逐一分发到后端应用上。也就是说刚开始我们在 Tomcat1 登录之后,用户信息放在 Tomcat1 的 Session 里。过了一会,请求又被 Nginx 分发到了 Tomcat2 上,这时 Tomcat2 上 Session 里还没有用户信息,于是又要登录。另外由于我们系统采用单点登录的方式,Tomcat2 登录之后会将 Tomcat1 登录信息失效,于是乎等到 Nginx 再把流量分发到 Tomcat1 时,Session 中用户登录信息已经失效,又要重新登录。知道了问题,阿粉当然想知道解决办法了,于是组长教了阿粉分布式一致性 Session 四种解决办法,阿粉给大家整理了一下:下面阿粉将会以阿粉跟组长对话的形式,讲解分布式一致性 Session 解决办法。
Session 复制
如果此时 Tomcat1 Session 存在用户信息,而 Tomcat2 上没有存在。这时如果我们将 Tomcat1 的 Session 复制到 Tomcat2 上,后面 Nginx 将请求转发到 Tomcat2 上,由于 Tomcat2 存在 Session ,这时就不需要再重新登录了。Tomcat 的 Session 复制的配置,网上有比较多的例子,这里阿粉就不再贴了,感兴趣的同学可以自行搜索一下。
对的,这种方式挺好啊。Tomcat 就支持这种方式,我们只需要修改 Tomcat 配置就好,我们应用代码都不用修改了。第二,我们的例子就只有两台机器,这个复制性能还可以。但是假设我们有 N 台机器,那么每次复制都要复制给 N-1 台机器,如果机器很多,可能会形成网络风暴,复制性能也会呈指数级下降。第三, Tomcat 需要保存所有的 Session 数据,这个方案的 Session 存储在内存中,容易受到机器的总内存的限制。我们没办法通过加机器的方式水平扩展,我们能做的方式就是加大机器内存。但是机器内存越大,价格真的很贵!!!
Session 前端存储
哎,有了!我们的 Session 里面其实就是存了用户的信息,那我现在不存 Tomcat Session 里,我把信息拿出来,存到浏览器的 Cookie 中。这样,每个用户浏览器存储自己的 Cookie 信息,服务端就不需要存储,这就解决了 Session 复制方案的缺陷了。接下来用户每次请求都把这个 Cookie 给我发过来,我判断 Cookie 里面用户信息不就好了。用户信息可是我们的敏感数据,不能让别人轻易的窃取或者篡改数据了。除了这个,这个方案每次请求都要携带 Cookie 传输,这会占用外网的带宽,如果 Cookie 过大,会增大网络的开销。另外,我们存储的数据大小,容易受到 Cookie 限制。
Session 粘滞(Sticky Sessions)
刚才应该看到了,我只是对 Nginx 的配置做了一些修改,然后这个问题就解决了吧。其实这是因为我修改 Nginx 默认的负载均衡策略,使用 IP Hash 的方式。Nginx 会使用请求者的 IP 来做 Hash,然后分发到一台机器上,这样可以保证同一 IP 的请求都落在同一台 Tomcat 上。上面这种方式我们使用 Nginx 四层负载均衡方式,其实 Nginx 还可以做到七层负载均衡方式,也就是使用 Http 协议中的一些业务属性来做 Hash,常见的有 userId,loginId等等。这种方案看起来挺简单的,我们只需要修改 Nginx 配置就好了,应用端配置无需改动。只要请求来源 IP 足够的随机,那么 IP HASH 之后两台应用上的流量将会足够随机。另外后面如果两台机器扛不住,我们还可以水平扩展,再加机器,只要修改 Nginx 配置即可。不过你有没有想过,像我们公司这种情况,所有人的出口的 IP 都是一个。那么我们公司的所有请求只会到一台机器上,那我们这种情况等于又变成单点了。另外如果 Tomcat 重启,Session 由于是放置在内存内存中,这一部分的 Session 将会丢失,这就导致这部分用户将会重新登录。最后,如果我们临时再加机器,修改完 Nginx 配置,重新启动之后,Nginx 将会重新计算 Hash 分发请求。这种情况就会导致有一部分用户重新路由到一台新机器上,由于没有 Session,又需要重新登录了。不过么,Tomcat 重启或者新加机器次数不会很多,所以这个问题也不大,用户体验稍差点。
后端集中存储
上面几种的方式我们都是把 Session 存储在应用内存上,应用机器只要重启,Session 就会丢失。为了这个解决这个问题,我们将 Session 单独存起来,保存到 Redis 或者 MySQL 中。不过由于 Session 需要过期失效的特性,不需要持久化保存,所以这里我建议使用 Redis 来保存。我们使用这种方案,上没有 Session 丢失的风险,当然前提是 Redis 不能宕机。如果后面应用的请求量很大,一台 Redis 扛不住了,那我们可以其实可以做集群扩展,根据缓存 Key 做路由。你不要高兴的太早,我们使用这个方案需要付出一定的代价的。首先我们每次请求都需要调用一次 Redis ,这就增加一次网络的开销。另外,引入 Redis,我们需要对相应的代码做出修改,这样复杂度就变高。所以说,这个方案有利也有弊,当然对于我们的场景来说,利大于弊。好了,这么晚了,问题解决了,我们去撸个串,我请客!我这一顿不是白吃哦,下个星期你把现在方式修改一下,修改成 Session 集中存储的方式。给你一个小提示,可以使用 spring-session。
总结
最后阿粉总结一下,当我们后端 Web 应用扩展到多台后,我们就会碰到分布式一致性 Session 的问题,主流解决方案有四种:- Session 复制:利用 Tomcat 等 Web 容器同步复制
- Session 前端存储:利用用户浏览器中 Cookie 保存 Session 信息
- Session 粘滞方案:利用 Nginx 可以做四层 Hash 或七层 Hash 的特性,保证用户的请求都落在同一台机器上
- Session 后端集中存储方案:利用 Redis 集中存储 Session,Web 应用重启或扩容,Session 也无需丢失。
当然第四种方案需要一定的开发工作量,前期还没改造的过程可以选择 第三种方案中间过渡。好了,后面阿粉就要使用 Session 后端存储方案改造这个工程了,后面阿粉再跟大家分享一下 spring-session。
帮助
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️
浏览
46点赞
评论
收藏
分享
手机扫一扫分享
分享
举报
点赞
评论
收藏
分享
手机扫一扫分享
分享
举报