session

Session,在计算机领域,被称为“会话控制”。

会话控制,这就提出了两个问题

  1. 什么是会话
  2. 为什么要控制会话
  3. 怎样控制会话

什么是会话

会话按字意来看就是进行对话,其实真正含义就差不多是这个意思。

在计算机术语中,会话是指一个终端用户与交互系统进行通讯的过程,比如从输入账户密码进入网上的一个系统到退出系统就是一个会话过程。

也可以理解为两个计算机之间的对话,这就是会话。

一个计算机的网络世界就像是被集体孤立的个人,虽然不能说孤立的个体是毫无作用的,但是不融入集体的个体,是不能发挥出个体的真正意义的,所以每个个体的交流是很重要的。所以在网络世界中,会话也是很重要的。

为什么要控制会话

会话大部分都是建立在HTTP的基础上的,但是HTTP是一个无状态的协议,于是就出现了一个很重要的问题,会话这么多,服务端该怎么区分这些会话是哪个用户的?所以就需要将这些会话进行控制区分。

怎样控制会话

服务端需要某种机制来将这些会话进行区分,这个机制就是session。

用户通过HTTP与服务端进行会话,服务端为每一个用户生成特定的sessionId来标记该用户,之后每个会话携带着特定sessionId,服务端就能确定该会话属于哪一个用户的了。

控制会话的大致思路就是这样了。

分布式环境下的session

在上述对session的描述中,会发现控制会话的方案是很好的。但是这仅限于单体架构,服务端只有一个服务器在提供服务,该服务存储着所有的sessionId,通过该服务器可以识别出所有被记录的用户。

但是在如今这个大流量的时代,大多时候,单个服务器是不能承担大规模的请求,所以就出现了集群方案,将单个服务器拓展为多个服务器提供服务,减轻单个服务器的压力。

但是从这个图中就会发现一个问题。

假设客户端A与服务端进行了一次会话,第一个请求发送到了服务器A,服务器A为客户端的这次会话生成了一个sessionId,但是客户端A的请求是有可能发送到服务器B的,但是此时的服务器B是没有记录这次会话的,所以也就是没有状态的,这违背了session的初心。其根本原因就在于session是没有共享。

接下来就探讨一下在分布式环境下的session怎样实现共享。

客户端存储

在分布式环境下,一个客户端的请求可能散落在不同的服务器上,但是我们可以发现客户端是唯一,所以将session直接存储在客户端不就可以实现session的共享了吗?这的确是一个方法,一个会话一定是会在同个客户端,这样就实现了session的共享。

那该怎么存储在客户端上呢?

可以借助浏览器的cookie来进行session信息的存储。在客户端向服务端第一次发送请求的时候,服务端生成一个特定的sessionId返回给客户端,浏览器收到了session信息之后,就将该信息存储到cookie中,每次发送请求都带上session信息,服务端就能识别出该会话。在这里服务端只提供生成session和识别seesion的服务,而不存储session信息。

缺点: session存储在cookie中,安全隐患很大,很容易被盗取。另一方面,cookie对于数据类型及数据大小存在限制。

session复制

第一种情况,在客户端存储,这种方案已经思考过了,那么接下来可以该在服务端想想方法了。

于是就出现了session复制这种方案。

将服务器A的sessionA给服务器B,服务器B的sessionB给服务器A,这样不就实现了session的共享了吗。

大致思路就是这样,在特定时间,服务器就会去复制其他服务器的session信息或者将自己的信息发送给其他服务器,从而实现session信息同步。

像tomcat等一些web容器就支持session的复制,在同一个局域网内发送该容器的session信息给其他容器。

缺点: session需要消耗一定的资源,当集群有点规模,消耗的资源也是不可小觑。

绑定session

第一种方法是在客户端,第二种方法是在服务端,接下来就可以考虑请求转发上想方法。

我们只要将同一个客户端的请求都转发到同一个服务器上不就好了吗。

这种想法也是可以行的通的。Nginx也提供了对应的方法。使用Nginx进行负载均衡,可以选择它的ip_hash的方案,将同一个IP的请求转发到相同的服务器,这样该次会话都只会在一个服务器上进行,宏观上就像是单体架构下使用的session。

缺点: 降低了集群的可用性,若是单个服务器出现故障,那么存储的session的信息丢失,会话就会出现故障。

基于外部存储

前三种方法在客户端,服务端,和负载均衡下思考的方案,除了这三者之外怎么进行session共享呢?

在计算机领域,没有什么问题是加一层不能解决的。

所以我们可以考虑给系统增加一个数据存储层,然后将seesion存储在该数据存储层上就好了,从而实现session的共享。

事实上,每一个系统都会存在数据存储层的,只是上述解决思路没有将该数据存储层表示出来。没有表示出来的原因只是因为在session的机制中没有涉及到存储层,所以没有提及。

这里就提供一个基于Redis的sessioon存储方案。(只要是提供存储数据的都是可以实现的)

这种方法除了降低整个系统的可用性就没有什么很明显的缺点,系统可用性也是可以通过搭建Redis集群来进行提高。其实上述的绑定session的方案和session的方案,总体也是会降低整个系统的可用性,所以就不探讨该缺点。所以这种方案也是最经常使用的方案了。