IT技术代码改变世界分布式

重写servlet API实现分布式session共享

2017-03-16  本文已影响344人  点融黑帮

session 机制是一种服务器端的机制,服务器使用一种散列表或者类似的结构来保存信息。在使用分布式 session 前会有这样两个疑问:

1)为什么采用分布式 session :

当我们的系统当中有多台服务器的时候,我们无法保证 session 在多台机器都存在,这是因为我们用户的请求不一定会被路由到同一台机器上。也就是说对应一个用户的 session 信息在服务器 A 上存在,但是在服务器 B 上不存在,因为请求被路由到了服务器 A 上,所以我们需要让集群中的多台机器共享 session信息。

2)为什么重写 session:

session 只能存储在本地 web服务器的内存当中,但无法满足 session 在多台服务器的分布式环境下,多台机器共享 session 数据的问题,所以我们需要重写,让它在分布式环境下,拥有 session 数据共享功能。

session配置方法

当程序需要为某个客户端的请求创建一个 session 的时候,服务器首先检查这个客户端的请求里是否包含了一个 session 标识,称为 session-id。

如果已经包含一个 session-id 则说明以前已经为此客户创建过 session,服务器就按照session-id 把这个 session 检索出来使用(如果检索不到,可能会新建一个,这种情况可能出现在服务端已经删除了该用户对应的 session 对象,但用户人为地在请求的 URL 后面附加上一个 JSESSION 的参数)。

如果客户请求不包含 session-id,则为此客户创建一个 session 并且生成一个与此 session 相关联的 session-id,这个session-id将在本次响应中返回给客户端保存。

其实分布式 session 并不是一个特别难的课题,实现的思路也有很多种。今天我将介绍一种使用起来非常简单的方法,只要做如下配置即可使用:

1 )导入一个 jar 在 pom 文件中

2) 在 web.xml 文件中配置一个 filter

session 使用方法

分布式 session 的使用:从使用者的角度不需要知道是否为分布式 session,一句即可搞定,而且和容器无关。

为了保证读者可以理解这种方法,下面我将以最简洁的方法来实现。

1、拦截器的实现

这个拦截器拦截了所有路径的客户端请求,并且在请求进去 Controller 之前把HttpServletRequest 替换成我自己实现的一个 HttpServletRequest,叫做DistributionSessionRequestWrapper

2、DistributionSessionRequestWrapper 的实现

HttpServletRequestWrapper 是 HttpServletRequest 的装饰类,通过继承它,覆盖你希望改变的方法,你可以改变当前 HttpServletRequest 对象的状态。

从 session 的使用角度上来说,你只会用 getSession() 这个方法,那么我覆盖这个方法就ok了,在 getSession() 中我做了两件事:

new 了一个的 HttpSession 实现叫做 DistributionSessionImpl

在 cookie 当中写入一个叫 pcxSessionId 的 cookie 进去,实际上就和 tomcat 的 jessionid 一样,这个就是用来从分布式缓存中寻找对应 session 数据的 key。

3、DistributionSessionImpl 的实现

在 DistributionSessionImpl 中有两个属性比较重要

sessionMap:用来存储一次请求中的 session 数据

sessionStore:把用户的 session 数据存储到分布式缓存的类

getId() 方法:通过这个方法可以获取用户的 cookie 中 key=“pcxSessionId”的value 值

getAttribute():通过key中 pcxSessionId从缓存中获取用户的 session 数据

setAttribute():如果为空的话,那么从分布式缓存中获取用户的 session 数据,当然如果这个用户是第一次访问的话,这个 sessionMap 可能还是为空,这个逻辑的话在sessionStore 有判断;把key-value数据存储到本地的sessionMap中,然后把数据推送到分布式缓存中

removeAttribute():清除本地 map 中的 Attribute 然后再把分布式缓存中的getID() 给删掉

Invalidate():先清除本地的 sessionmap 然后删除分布式缓存中的 session 数据,最后清除 cookie

4、SessionStore 的实现

session 存储的时候使用 hession 的序列化,然后通过调用 jedis的 api 存放session 数据。这个类应该是很容易理解的,当然具体你用 redis 还是 memcached 这个随意了。

至此,一个基本的分布式 session 就实现了。它的本质是加一个拦截器然后把 HttpServletRequest 替换成自己定义了,然后覆盖掉 getSession 方法,最后用一个我自己定义的 HttpSession 实现来完成各种操作。

总结

上述代码只是保留了最基本的实现,我们也可以继续优化封装,添加更多的功能。下面笔者以减少用户与分布式缓存机器的通信次数的优化为例,供大家参考。

从 DistributionSessionImpl 中我们可以看出,每次 setAttribute 和removeAttribute 数据都会同步到分布式缓存,这个实际上是没有必要的,虽说分布式缓存和我们的 server 虽然是在内网中通信,但是来回的次数会增加其时间损耗。

我们不能保证把一个用户的请求一直路由到同一台机器上,但是能够保证一次 request 在一台机器上,我们可以在用户的一次请求结束后把这个用户的 Session 数据同步到分布式缓存当中,以减少了与分布式缓存机器的通信次数。

本文作者:彭晨雪(点融黑帮),现就职于点融网技术部 lb 团队,java 开发工程师一枚。

上一篇下一篇

猜你喜欢

热点阅读