分布式会话

       上周用了整整一周时间,搞了一个分布式会话的东西。尽管到了最后没有完成的很完美,有一点点小问题延误了上线,不过这对我自己来说,已经非常满足了。拿出来分享一下。


       当今,单节点架构的项目是很容易当掉的。网络攻击、竞争公司“不正当访问”、用户量的大幅度提升以及快速迭代、产品不断打补丁上线,这都对系统的架构提升了很高的要求。为了更好的应付需求变化,也为了确保系统的稳定性。公司决定把这个分布式会话提上日程。由于我对此兴趣比较浓,自然把这个任务抢先占了下来。


      之前的架构是如下这样,代码写的再漂亮,多线程考虑的再仔细,也抗不住各种IO瓶颈。单一节点的结构,无法使用复杂多变的显示需求。

                             


      一周的研究,终于出了个雏形。我在网上找了很多资料,结果跟我想的差不多。这种事情,肯定有多种方式、多种方法实现,这是毋庸置疑的。

其实,使用Tomcat做为Web服务器也是可以的,只不过Tomcat处理静态化的能力相比于Apache、Nginx、IIS等,会差很多,所以几乎没有公司选择它。当然,我们也可以直接使高级服务器做转发,直接由硬件在数据链路层做转发,这种方式,比一般的软件做转发,效率会更高一层。


      不过,我们公司不是土豪公司,多搞一台Linux服务器,还是申请了老长时间才批下来的。我们选择的Web服务器也很传统——Nginx。先看架构结构图:

          


      这里,缓存服务器选择的是Memcache,使用KMemcached作为缓存的客户端,当然,你也可以选择其他的存储结构来保存Session。比如 当今如日中升的Redis、或者其他的数据库以及内存数据库均可。我这里选用Memcache,是因为头儿对Memcache很熟悉,资料也很完备的原因。不过,我个人更加倾向于Redis,不过时间有限,先搞上一个再说。


      我们都知道,网络应用,说到本质上,都是在面向HTTP编程的。而HTTP是无状态的,那我们如何保持用户状态,维持用户的会话过程呢?我们一般的处理都讲客户端的信息保存在Cookie中,服务端的状态信息保存在Session,如User或者UserId,我们需要将对象或者单一的UserId属性保存在Session,来维持用户会话状态。


      而单一节点的结构,无法应对多频度的系统上线和系统宕机。唯一的解决办法,就是重启服务器。这一点的用户体验,是非常差的(一两年前,就在CSDN写博客的童鞋应该大有体会,看到维护中待解决的心情,不解释。当然现在CSDN是越来越好了~~嘻嘻~~)。


      所以说,不仅人要懂得分工合作,系统更要分布。而问题的节点就在于Session。所以我们把Session从服务端拿出来,将Session保存在内存数据库中,也就是说将User信息,保存在内存数据库中,多个Tomcat服务器的会话状态,由内存数据库统一维护。这样就能够将系统进行分布部署。


      使用Nginx做Web服务器,它能够处理静态资源,将js、css、图片等静态资源过滤出来,直接发送给客户端,而将动态请求,根据一定的策略、权重,动态分发到不同的服务器上,分摊服务器压力。


      那么,我们如何将Session保存在内存数据库中呢?直接在代码中改吗?很大的工作量啊。我这里使用的是添加一个SessionFilter,注意,这个Filter,一定要在所有含状态请求的前面,也就是说,这个filter,做好放在所有filter的上面。因为filter的执行顺序是根据filter的位置,像栈一样执行的。如filter1在filter2之上,执行顺序就是:filter1>filter2>程序>filter2>filter1.


      在这个SessionFilter中,重新包装Request,所有对session的增删改查操作,都需要涉及到Cookie、Memcache库中的Session信息的状态。


      这里只讲一个思路,感兴趣的童鞋,可以跟我发邮件交流。


   

      搞这个过程中,遇到的问题:


1、Filter管理的问题。一般我们会在web.xml里面的<filter-class>标签里面,直接filter的类路径,这时,filter在初始化时直接由容器管理,初始化到内存中了。没什么说的,如果我们希望交给Spring管理,那么我们必须把<filter-class>写成org.springframework.security.util.FilterToBeanProxy,这时,我们可以通过配置,将filter初始化到某个Bean中。


2、改造SessionFilter,sessionFilter是分布式会话的核心。它的每次修改,都需要重新编译,重启服务。封装session的setAttribute方法时,没有考虑session.setAttribute("user",null)的情况,因为所有传递的对象都需要序列化,而null不是序列化对象,当然会失败。


3、Memcache缓存策略尚待考虑。


4、Nginx配置+HTTPS配置。


5、服务器端防火墙等的配置。



  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值