-
Notifications
You must be signed in to change notification settings - Fork 626
本项目对分布式系统下Session的探讨
网上有很多各种各样的定义,下面是一个比较贴切,而且权威的。
旨在支持应用和服务的开发,可以利用物理架构由多个自治的处理元素,不共享主内存,但通过网络发送消息合作。
--Leslie Lamport 2013年图灵奖获得者
上面的定义突出了分布式系统的三个特点,这三个特点应对着三个很容易混淆的概念。
三个特点和三个概念
三个特点 三个概念
-----------------------------------------
多节点 分布式系统(distributed system)
消息通信 集群(cluster)
不共享内存 分布式计算(distributed computing)
先来看第一个-多节点,分布式系统是多节点的,集群也是多节点的,那这两个有什么区别呢?打个比方,如果厨房里面有两个后厨,一个是炒菜的,一个是洗菜的,这种就称为分布式,那如果两个都炒菜,那就是集群,放在我们系统上,卖家端、买家端是分布式的,如果卖家端部署在多台服务器上,一样的程序,就是卖家端集群,所以可以看得出来,分布式和集群是有区别又有联系的。
然后是消息通信和不共享内存,这就是前面定义的,不共享主内存,但通过网络发送消息合作,这是什么意思呢?就是说,分布式系统中,各个节点是通过发送消息来通信的,比如Http、REST接口、RPC,放到我们系统中,这点也是OK的,前端WebApp,通过REST API访问我们后台程序,获得接口数据。
那特意提到的不共享内存是为了和哪个区别呢?和分布式计算。分布式计算有的地方会把它叫做并行计算,举个例子,大名鼎鼎的大数据,Hadoop中的mapreduce就是属于分布式计算,你可以这样通俗地理解,两个厨子都炒一样的菜,两个人炒完之后呢,把菜放到一个盘子里面去,这样就得到了一盘菜,这个时候你分不清楚,到底那个菜是那个厨子炒的,而我们的分布式系统,还记得吗?是一个洗菜的和一个炒菜的。最后一个概念可以参考大数据的课程,这里不做要求。
分布式系统强调的是不同功能模块的节点,而集群指的是相同业务功能的节点;分布式系统中的每一个节点都可以做集群,比如卖家端,我可以部署一个到多个成为一个集群,在工作中很多分布式系统的节点会做成集群的形式,集群的规模往往由这个节点的业务规模来决定,而集群并不一定是分布式的,比如前后端未分离的一个系统,all in one这样的形式,他所有的业务模块都在一起,就算他有很多机器做了集群,但是这些节点并不需要进行消息通讯,所以他并不是分布式的。
有时候我们谈Session谈的是狭义的Session,说的是HttpSession,是一个J2EE接口,而我现在所说的Session,是广义的Session,是中文里面的会话控制,我们知道Http协议是无状态的,对于同一个url请求,并没有上下文关系,当一个用户完成登陆之后,就要有一个机制能够保存住用户的信息和状态,在后续的请求中能够验证用户的身份和检查用户的信息,这个依赖就是会话控制。
换个角度看Session,你可以把它看成一种key-value的机制,Session机制中的关键点:第一,是如何设置和获取key;另外一点,就是就是如何能够保存和正确的获取对应的value。从key方面来看,我们常听到说,会话会有两种比较常规的方式,一种是sessionId,另一种是token。
session这个很常见:
客户端请求服务端的时候,服务端通过setCookie就可以在Http相响应头里面设置sessionId和对应的value值,而客户端的Cookie会将这个key-vaue对保存住,后续的请求里,会自动的带上;
另外一种是token:
使用token的时候,我们需要手动在Http请求头里,或者是url参数里面设置token这个字段,服务器收到请求之后,再从请求头里或者url参数里,取出token进行验证,当然安全方面要求比较严格的时候,token会配合签名一起使用。
从上面的分析中,我们可以看到的是,无论是sessionId还是token,他们一定是全局唯一的,一个用户一个标识,他的本质就是一个key,很显然,这个key对应的用户信息就是value,无论key还是value,一方面要保持不住,我们就可能分辨不出这个用户的身份,获取不到这个用户的信息了。比如依赖sessionId的时候,如果用户禁用了cookie,那么就可能造成系统不断地让他重新登陆,那value一方,为什么说有可能保持不住呢,这就是我们要说的分布式系统Session问题。
我们先假设一下下面是我们应用最开始时候的应用架构。
前面来自用户的请求,通过Nginx到达Tomcat,Tomcat上面部署了我们的一个应用,这个时候,Session是保存在这个Tomcat应用的内存里面的,后来,我们的事业发展的非常的顺利,用户增长的特别快,这个时候,一个Tomcat扛不住了,怎么办,加机器呗。
这样我们就又加了两台服务器,那么问题来了,这两台加上去之后,程序怎么放呢,其实有两种方法,一种是水平扩展,一种是垂直扩展,更复杂的时候,你可以水平加垂直同时做。
水平扩展,现在我加了两台,称他为A1和A2吧,加入A1和A2都是从A复制过来的,那A1跟A2就完全是一样的,这种做法,就是我们前面说所的你做了一个集群。
那垂直扩展又是什么呢,其实就是拆分服务,比如我们在A这个服务器上有类目、商品和订单三个服务,我可以把他给拆出来,拆完之后呢,A上面部署类目这个服务,A1上部署的是商品,A2上部署的是订单,这样子,把程序的功能做一个垂直的拆分,之后我们可以配置Nginx,根据访问不同的url,负载均衡到不同的服务器上去,这样子,单台服务器的压力就会减少。
但是不论是水平扩展,还是垂直扩展,Session的问题都会出来,比如用户第一次进来,访问的是A服务器,这个时候,A持有了用户的Session,接着,用户做第二个请求,由于负载均衡,请求可能到了A1,A1没有这个用户的Session信息,所以他就以为这个用户没有登陆。对于水平扩展,我们可以通过IP hash,他可以将同一个IP过来的请求每次转发到后端同一台服务器上去,但这个其实也是有隐患的,假如你使用了IP hash,某个用户他会一直访问A服务器,但是访问量大的时候,A服务器已经支持不下去了,可能已经挂掉了,那么原来访问A服务器的用户后续就不能再访问我们的系统了。对于垂直扩展,IP hash就更不行了。所以IP hash并不是一个好的的方案。
真正通用的方案是,我们需要一个专门的服务去保存session信息,而其他服务需要session的时候,都去找他要。
这个服务通常是用redis集群或者主从复制去做的,当然我们开发测试的时候,用个单机版的就行了。
这样的话,无论是水平扩展还是垂直扩展,用户都能够通过用户的唯一标识来找到这个用户对应的信息,从而进行后续操作,登陆的时候设置好key,保存好这个用户的信息对应的value,登出的时候让value失效就行了。
目录