分布式架构下的session 问题

308 阅读5分钟

这是我参与8月更文挑战的第20天,活动详情查看: 8月更文挑战

大家都知道一提到session,肯定就会想到cookie,比较它俩都是配对出现的,在进入正题之前先简单的谈下它两的关系吧。HTTP协议属于无状态协议,服务端为了记住客户端的访问信息,就有了session和cookie。session是存在服务端的,cookie是客户端,目的都是为了记住客户端的信息。 原来传统session的做法就是,request.getSession(),在会话开始的时候为当前会话分配一个唯一的会话表示,也就是sessionId,通过cookie方式将sessionId存放到浏览器中,然后浏览器每次访问的时候,都会带上sessionId,这样的话,服务端就会认为是同一个访问,服务器通过获取到的sessionId来判断是哪个会话,然后获取存在服务器端的访问信息。 当分布式架构后,后端访问就不止一台服务器,因为传统的session技术是存在tomcat内存中的,当客户端第一次访问A时,将会话信息存放到A中,第二次访问时,由于负载均衡算法将访问路由到B服务器,这个时候B上面并没有存有第一次的访问信息,就导致B服务器无法获取到客户端的访问信息,可能就会重定向到登录页面,这样就导致了session问题。其实在分布式环境下sesion共享的技术方案都比较成熟了。 最先想到的方案就是,既然session信息存放在A服务器,那就把session 的信息同步给B服务器,这样的话,客户端及时访问到B服务器上面也存有A服务器的session信息。

不同服务器之间同步session数据

架构演变之路 (6).png

这种方案就是在web之间实现session的同步,但是这种方案存在的问题比较多:

  1. 服务器之间存在session的话,网络开销也比较大,只要一台服务器的session发生了变化,都需要同步同步到其他服务器,如果机器越多,需要同步的节点就越多,造成的网络开销也比较大
  2. 如果集群节点比较多,每台服务器都需要保持整个集群节点的所有session信息,这样的话,保持的会话比较多,对于内存的占用比较严重
  3. 服务器之间同步session属于额外的操作,会浪费服务器的额外性能

针对这种web之间同步session在新开发中基本上不会用,这个方案只适合机器数较少的情况,我想到一个适用的场景:就是针对老项目,运行比较稳定,源码也不知道丢到哪里去了,本来都是几个人访问,后来增加到几十个人访问,就需要增加个节点来增加并发,因为没法改造,采用这种方案是最省时省力的方案。

session数据集中存放

现在用的比较成熟的方案就是将session 的信息存放到所有应用节点都可以读取的信息,比较常见的就是Redis,可以自动帮我们管理session会话过期的问题。

架构演变之路 (7).png

将Session数据不保存到本地,集中存放起来,这样话任何一个节点对session数据发生了修改,其他节点读取的时候也会获取到最新修改过的数据,每个节点的session数据都是一样的,但是这种也存在一定的问题。

  1. 因为session是存放到其他地方,每次操作session时需要网络操作,如果网路不稳定的情况下,就会导致每次请求响应慢,但是这种情况,一般服务器之间通信都是内网,延迟比较低。

  2. 将session集中存放到一个地方,如果存放的集群存在问题,突然整体不可用(这种情况也基本上不可能发生),就会导致我们的整个应用不可以使用,相当于整个网站瘫痪。

    虽然存在上面的两种可能性,但是这种方案用的比较多,毕竟session存放的Redis也会做到高可用,整个集群不可用的话,基本上不会发生,除非人为恶意导致,现在的Redis都会主从架构+哨兵模式 做到高可用

session数据存放到客户端

看了上面的方案,不是客户端还有cookie可以存放数据嘛,那就把session的数据通过加密算法存放到浏览器中,这样的话,每次浏览器访问的时候就将session的数据带上来,有服务端对里面的数据进行解密获取到session。

架构演变之路 (8).png

这样将session存放到浏览中就不会出现session挂掉及网络延迟的问题,但是这种方案暴露了个最大的问题,也是个致命的问题,那就是安全性,因为将session数据暴露给了外部,这样的话就会导致安全问题发生,虽然我们是加密存储,既然有加密那肯定有解密,长期运行的网站,无论怎样加密,总有天会被别人解密出里面的数据,就会导致用户信息流失,严重的会导致session会话篡改,造成损失。