集群环境下的Session共享

 

一、一些概念

1.HTTP协议的无状态性:

每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录

2.Request(请求):

指客户端向服务器发送的信息,通常是通信的发起方

3.Response(响应):

指服务器对请求的应答,通常是通信的回复方

4.Session(会话):

客户端第一次访问服务器,服务器会开辟一块空间创建Session对象,并为这次Session对象生成唯一标识符SessionId,用来表示这次会话,并通过响应头的Set-Cookie:“JSESSIONID=XXXXXXX”向客户端发送要求设置Cookie的Response,接下来客户端每次发送请求时,请求头都会带上SessionID,这样就弥补HTTP的无状态特性

5.Cookie

客户端保存在本地的数据,SessionId就储存在Cookie

PS:服务器只会在客户端第一次请求响应时在响应头上添加Set-Cookie:“JSESSIONID=XXXXXXX”,接下来在同一个会话的第二第三次响应头里不会添加,而客户端会每次在请求头的Cookie中带上JSESSIONID信息

二、问题引出

构建servlet,打印SessionID,放入集群环境

@RequestMapping(value = "/login.do", method = RequestMethod.GET)
public String login(HttpServletResponse response, HttpServletRequest request){
    return request.getSession().getId();
}

img

可以看到访问同一地址拿到了不同的SessionID,也就是负载到了不同的服务器,设想如果把信息存入Session的话,那么负载到其它的服务器的话就无法获取之前存入Session的内容,相当于一个新客户端访问。

三、解决方案

1.把数据都存入Cookie中,每次请求都传递这些数据。

缺点:Cookie长度的限制也会限制Session数据的长度。Session数据本来都是服务端数据,存储在客户端存在安全性上的问题。每次HTTP请求和响应都带有Session数据,影响Web服务器性能

2.把数据存入Redis,Mysql等等,Cookie中只存一个索引

缺点:存储这些数据的服务宕机了也就无法服务了,中间网络出现状况也会影响

四、落地实现

这里选择第二中方案

添加Redis服务,在其中添加一个数据

img

修改一下获取逻辑

@RequestMapping(value = "/login.do", method = RequestMethod.GET)
public String login(HttpServletResponse response, HttpServletRequest request){
    Jedis jedis = RedisPool.getJedis();
    return request.getSession().getId()+ "<br/>" + jedis.get("kekeke");
}

img

可以看到SessionID不同,但获取自己存入的值是相同的

Demo地址