分布式Session管理实战

章节目录:

  一、Cookie和Session原理分析;

 二、分布式Session管理的起因;

 三、分布式Session的常用方式及实现方式;

Cookie和Session的实现工作机制

    在实现之前,先了解Cookie和Session的实现工作机制:

    会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

  一、    Cookie实现机制:

        Cookie是Web服务器生成,向用户浏览器发送的一小段ASCII文本。当浏览器接收到后,会将其信息片段以“键-值”对的形式保存在某个目录下的文本文件中。以后每次向同一个服务器发送请求的时候,浏览器都会发送以前存储在本地的Cookie。浏览器和服务器通过HTTP协议进行通讯,Cookie便被保存在HTTP的请求部分(Set-Cookie)。

        例如,我们创建了一个名字为jss的Cookie来包含访问者的信息,创建Cookie时,服务器端的Header,这里假设访问者的注册名是"xxoo.com",同时还对所创建的Cookie的属性如path、domain、expires等进行了指定,Cookie文件中添加一条记录。浏览器将变量名为“shopCart”的Cookie赋值为"xxoo.com"”。注意,在实际传递过程中这个Cookie的值是经过了URLEncode方法URL编码操作的。 这个含有Cookie值的HTTP Header被保存到浏览器的Cookie文件后,Header就通知浏览器将Cookie通过请求以忽略路径的方式返回到服务器,完成浏览器的认证操作。

     Set-Cookie:shopCart=xxoo.com;path=/;domain=xxoo.com;   

     expires=Monday,01-Mar-99 00:00:01 GMT  

浏览器对于Web服务器应答包头中Cookie的操作步骤:

       1. 从Web服务器的应答包头中提取所有的cookie。

       2. 解析这些cookie的组成部分(名称,值,路径等等)。

       3. 判定主机是否允许设置这些cookie。允许的话,则把这些Cookie存储在本地。

 浏览器对Web服务器请求包头中所有的Cookie进行筛选的步骤:

        1. 根据请求的URL和本地存储cookie的属性,判断那些Cookie能被发送给Web服务器。

        2. 对于多个cookie,判定发送的顺序。

        3. 把需要发送的Cookie加入到请求HTTP包头中一起发送。

 JS:创建Cookie:

function setCookie(c_name,value,expiredays)
{
var exdate=new Date()
exdate.setDate(exdate.getDate()+expiredays)
document.cookie=c_name+ "=" +escape(value)+
((expiredays==null) ? "" : ";expires="+exdate.toGMTString())
}

Java:创建Cookie:  

Cookie cookie = new Cookie("cookiename","cookievalue");
   cookie.setMaxAge(3600);
   //设置路径,这个路径即该工程下都可以访问该cookie 如果不设置路径,那么只有设置该cookie路径及其子路径可以访问
  cookie.setPath("/");
   response.addCookie(cookie);
  }
Cookie[] cookies = request.getCookies();//这样便可以获取一个cookie数组
for(Cookie cookie : cookies){
  cookie.getName();// get the cookie name
   cookie.getValue(); // get the cookie value
}

 详细原理文章介绍:

   http://blog.csdn.net/huanggang028/article/details/8066955

二、    Session实现机制:

     当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 – 称为session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。 保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID,

   比如weblogic对于web应用程序生成的cookie,

    JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是JSESSIONID。

Session常见问题:

  1. 浏览器禁止关闭Cookie怎么办?

  解决方式:

      URL 重写:就是把session id直接附加在URL路径的后面

  比如:

       http://www.test.com/test;jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764

  2、表单隐藏字段:

      就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如:

<form name=”"testform”" action=”"/xxx”"> <input type=”"hidden”" name=”"jsessionid”" value=”"ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764″”> <input type=”"text”"> </form>

 

  1. “只要关闭浏览器,session就消失了”?

    回答:错误的,其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session。恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

 详细原理文章介绍:

    http://beyond99.blog.51cto.com/1469451/543282/

分布式Session管理的起因:

   在实现多应用之后,会遇到Session存储的问题:当一个带有会话的 Http 请求到达 Web 服务器后,需要在请求中的处理 session 数据。问题在于,session 是保存在单机上的。假设我们是一个分布式集群,有应用 A 和应用 B,A 和 B 可能位于不同的服务器(集群),现在一位用户第一次访问网站,session 数据保存在应用 A 中。如果不做处理,怎么保障接下来的请求每次都请求到应用 A 呢? 如接下来用户又请求到了应用 B ,就会发现没有这位用户的 session 数据,这绝对是不能容忍的。

 

在常用的互联网解决方式中可以基于如下的方式实现多应用直接Session的共享。

方式一:Session Stick

     简介:在单机情况,session 保存在单机上,请求也是到这台单机上,不会有问题。变成多台(集群,此时只是增加 Web 集群,并没有进行垂直划分,每台服务器具有相同的功能)后,如果能保障每次请求都到同一台服务,那就和单机一样了。这需要在负载均衡设备上修改,保证用户每次都落到同一台服务器上。这就是 Session Stick,

     问题: 如果某一台服务器宕机或重启,那么这台服务器上的 session 数据就丢失了。如果 session 数据中还有登录状态信息,那么用户需要重新登录。负载均衡要处理具体的 session 到服务器的映射。

    Stick即粘性Session、当用户访问集群中某台机器后,强制指定后续所有请求均落到此机器上

     使用场景:机器数适中、对稳定性要求不是非常苛刻

     优点:实现简单、配置方便、没有额外网络开销

     缺点:网络中有机器Down掉时、用户Session会丢失、容易造成单点故障

实现方式:

   可以基于Nginx的IP Hash方式,

   每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 

   upstream tomcat_web_test{

        ip_hash;

         server 127.0.0.1:8081;

       server 127.0.0.1:8082;

     }

方式二:基于Cookie管理;

       简介:在多台后台服务器的环境下,我们为了确保一个客户只和一台服务器通信,我们势必使用长连接。使用什么方式来实现这种连接呢,常见的有使用nginx 自带的ip_hash来做,我想这绝对不是一个好的办法,如果前端是CDN,或者说一个局域网的客户同时访问服务器,导致出现服务器分配不均衡,以及不能 保证每次访问都粘滞在同一台服务器。如果基于cookie会是一种什么情形,想想看, 每台电脑都会有不同的cookie,在保持长连接的同时还保证了服务器的压力均衡,nginx  sticky值得推荐。

       把 session 数据存放在 cookie 中,然后请求过来后,从cookie中获取session数据。虽然与集中管理相比,这个方案并不依赖外部的存储系统,读写 session 数据带来的网络操作延时和不稳定性,但用户可能会禁用 Cookie。

       缺点是: Cookie有长度限制,这会影响session数据的长度。
      安全性。session数据本来存储在服务端的,而这个方案是让session数据转到外部网络或客户端中,所以会有安全性问题。不过可以对写入Cookie的session 数据做加密。
      带宽消耗。由于加了session数据,带宽当然也会增加一点

       客户端以Cookie的形式存放SessionId,访问不同服务的时候携带这个Cookie、从而判断这个SessionId是否存在或者是否登录过。

  使用场景:淘宝的 Session cookie

     优点:不依赖外部存储系统

     缺点:有长度限制、安全性差、带宽消耗。 

实现方式:

  可以基于Nginx  Cookie的负载均衡模式 --- Nginx Sticky;

  详细文章介绍:

    https://my.oschina.net/766/blog/156693

    wgethttps://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/nginx-sticky-module/nginx-sticky-module-1.1.tar.gz

    追加sticky模块–add-module=/opt/nginx-sticky-module-1.1

     编译nginx-sticky-module时出现错误,解决方法如下链接:

        http://crabdave.iteye.com/blog/2254192

        http://stackoverflow.com/questions/32314064/nginx-src-core-ngx-sha1-h1917-no-such-file-or-directory

方式三:基于Session复制;

     Session 复制顾名思义,就是每台应用服务,都保存会话 session 数据,一般的应用容器都支持。与 Session Stick 相比,sessioon 复制对负载均衡没有太多的要求。
  缺点是:
     同步 session 数据带来都网络开销。只要 session 数据变化,就需要同步到所有机器上,机器越多,网络开销越大。
     由于每台服务器都保存session数据,如果集群的session数据很多,比如90万人在访问网站,每台机器用于保存session数据的内容占用很严重。这个方案靠应用容器来完成,并不依赖应用,如果应用服务数量并不是很多,可以考虑。

    将一台机器上的Session数据广播复制到集群中其余机器上

  使用场景:机器较少,网络流量较小

     优点:实现简单、配置较少、当网络中有机器Down掉时不影响用户访问

     缺点:广播式复制到其余机器有一定廷时,带来一定网络开销

  解决方式:

      tomcat的session复制是基于IP组播(multicast)来完成的,简单的说就是需要进行集群的tomcat通过配置统一的组播IP和端口来确定一个集群组, 当一个node的session发生变更的时候, 它会向IP组播发送变更的数据, IP组播会将数据分发给所有组里的其他成员(node).;

Tomcat1:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> 
          <Manager className="org.apache.catalina.ha.session.DeltaManager" 
                   expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> 
          <Channel className="org.apache.catalina.tribes.group.GroupChannel"> 
              <Membership className="org.apache.catalina.tribes.membership.McastService" 
                        address="228.0.0.4" 
                        port="45564" 
                        frequency="500" 
                        dropTime="3000"/> 
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" 
                      address="127.0.0.1" 
                      port="4001" 
                      autoBind="100" 
                      selectorTimeout="5000" 
                      maxThreads="6"/>
                   <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">   
                <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>   
              </Sender>   
              <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>   
              <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>   
              <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>  
          </Channel> 
          <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"  filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>   
          <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> 
           <!--
             <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" 
                    tempDir="/tmp/war-temp/" 
                    deployDir="/tmp/war-deploy/" 
                    watchDir="/tmp/war-listen/" 
                    watchEnabled="false"/> 
                -->
          <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> 
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> 
        </Cluster>     
 
Tomcat2:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> 
          <Manager className="org.apache.catalina.ha.session.DeltaManager" 
                   expireSessionsOnShutdown="false" notifyListenersOnReplication="true"/> 
          <Channel className="org.apache.catalina.tribes.group.GroupChannel"> 
              <Membership className="org.apache.catalina.tribes.membership.McastService" 
                        address="228.0.0.4" 
                        port="45564" 
                        frequency="500" 
                        dropTime="3000"/> 
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" 
                      address="127.0.0.1" 
                      port="4002" 
                      autoBind="100" 
                      selectorTimeout="5000" 
                      maxThreads="6"/>
                   <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">   
                <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>   
              </Sender>   
              <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>   
              <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>   
              <Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/>  
          </Channel> 
          <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"  filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/>   
          <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/> 
           <!--
             <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" 
                    tempDir="/tmp/war-temp/" 
                    deployDir="/tmp/war-deploy/" 
                    watchDir="/tmp/war-listen/" 
                    watchEnabled="false"/> 
                -->
          <ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/> 
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/> 
        </Cluster>    
JSP:
<body>
 
      <%
           //HttpSession session = request.getSession(true);
 
           System.out.println(session.getCreationTime());
 
           out.println("<br> SESSION ID:" + session.getId() + "<br>");
 
           out.println("Session serviced by master" + "<br>");
 
           out.println("Session created time is :" + session.getCreationTime()
                      + "<br>");
      %>
 
</body>

   严重: FarmWarDeployer can only work as host cluster subelement!

解决方式,注释

    <!–

             <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" 

                    tempDir="/tmp/war-temp/" 

                    deployDir="/tmp/war-deploy/" 

                    watchDir="/tmp/war-listen/" 

                    watchEnabled="false"/> 

                –>

  注:在多数的解决方案中也有基于sticky+session复制模式,

        用户的请求按照 sticky方式被分发到同一个web服务器上,同时tomcat在后台做异步复制(非同步)session到其他web服务器。

 

方式四:基于Session集中式管理;

     用一台专门的服务器管理 session 数据,每台应用服务都从专门的 session 管理服务中取会话 session 数据。可以使用数据库,NOSQL 数据库等。与 Session 复制相比,减少了每台应用服务的内存使用,同步 session 带来的网络开销问题。

    缺点是:
        读写session引入了网络操作,相对于本机读写session,带来了延时和不稳定性。如Session集中服务有问题,会影响应用。

    简介:将Session存入分布式缓存集群中的某台机器上,当用户访问不同节点时先从缓存中拿Session信息

    使用场景:集群中机器数多、网络环境复杂

    优点:可靠性好

    缺点:实现复杂、稳定性依赖于缓存的稳定性、Session信息放入缓存时要有合理的策略写入

一、 利用数据库同步session

     在做多服务器session同步时我没有用这种方法,如果非要用这种方法的话,我想过二种方法:

      1,用一个低端电脑建个数据库专门存放web服务器的session,或者,把这个专门的数据库建在文件服务器上,用户访问web服务器时,会去这个专门的数据库check一下session的情况,以达到session同步的目的。

    2,这种方法是把存放session的表和其他数据库表放在一起,如果MySQL也做了集群了话,每个mysql节点都要有这张表,并且这张session表的数据表要实时同步。

        说明:用数据库来同步session,会加大数据库的负担,数据库本来就是容易产生瓶颈的地方,如果把session还放到数据库里面,无疑是雪上加霜。上面的二种方法,第一点方法较好,把放session的表独立开来,减轻了真正数据库的负担 

二、利用Nosql数据库redis或者memcache同步session

      redis或memcache可以做分布式,如果没有这功能,他也不能用来做session同步。他可以把web服务器中的内存组合起来,成为一个"内存池",不管是哪个服务器产生的sessoin都可以放到这个"内存池"中,其他的都可以使用。

     优点:以这种方式来同步session,不会加大数据库的负担,并且安全性比用cookie大大的提高,把session放到内存里面,比从文件中读取要快很多。

     缺点:redis或memcache把内存分成很多种规格的存储块,有块就有大小,这种方式也就决定了,memcache不能完全利用内存,会产生内存碎片,如果存储块不足,还会产生内存溢出。

    可以通过MSM解决方案解决集中是Session共享问题,也可以自己通过代码编程,来实现Session管理;

采用MSM方式:

        MSM(memcached-session-manager) 支持tomcat6 和tomcat7 ,利用 Value(Tomcat 阀)对Request进行跟踪。Request请求到来时,从memcached加载session,Request请求结束时,将tomcat session更新至memcached,以达到session共享之目的, 支持 sticky  和 non-sticky 模式。需要注意的是使用sticky模式时需要配置jvmroute参数:

MSM解决的问题

     假设你有一个Tomcat集群,使用黏性session,如何应对单点故障问题?为了应对更多的并发量和可用性,你可以不断的增加Tomcat节点,但是单点故障仍旧会是个问题:如果使用黏性Session,一个Tomcat故障时,其他Tomcat并不能接管故障Tomcat节点的Session。

解决此问题的思路就是将黏性Session同时保存在Memcached中,如果单个Tomcat发生故障,集群中的其他Tomcat可以从Memcached中得到Session信息。

MSM如何工作

     【注】以下论述仅针对黏性Session

      安装在Tomcat上的MSM使用本机内存保存session,和StandardManager一样。另外,当一个请求结束时,session会被送回Memcached进行备份。当下一次请求开始时,本地Session可用,直接服务,请求结束后,session又被送回Memcached备份。

当集群中的一个Tomcat挂掉,下一次请求会被路由到其他Tomcat上。负责处理此此请求的Tomcat并不清楚Session的信息。此时它会从Memcached查找该Session,更新该Session并将其保存在本机内容。此次请求结束,session被修改,送回Memcached备份。

 blob.png

     方式一:Sticky 模式:tomcat session 为 主session, memcached 为备 session。Request请求到来时, 从memcached加载备 session 到 tomcat (仅当tomcat jvmroute发生变化时,否则直接取tomcat session);Request请求结束时,将tomcat session更新至memcached,以达到主备同步之目的。下面是sticky模式时响应的流程图(图片来源网络):

 blob.png

 方式二:Non-Sticky模式:tomcat session 为 中转session, memcached1 为主 sessionmemcached 2 为备session。Request请求到来时,从memcached 2加载备 session 到 tomcat,(当 容器 中还是没有session 则从memcached1加载主 session 到 tomcat, 这种情况是只有一个memcached节点,或者有memcached1 出错时),Request请求结束时,将tomcat session更新至 主memcached1和备memcached2,并且清除tomcat session 。以达到主备同步之目的,如下是non-sticky模式的响应流程图:(图片来源网络)。

 blob.png

安装配置:

 准备的jar包

   注意:不同的tomcat版本(tomcat6,tomcat7)所需的包不一样,需要针对tomcat版本下载对应的包.

  1.这是采用的最新稳定版apache-tomcat-7.0.67,序列化方式使用的是kryo,注意版本要求与msm版本基本一致,建议统一采用最新稳定版,如下。其中序列化方式是可选的。

  blob.png

配置

   1. 将上面所提到的包全部拷贝到tomcat的lib下(三台tomcat都需要)

   2. 修改每台tomcat的conf目录下得context.xml文件或者server.xml文件,在其中加入如下任意一段代码(注意:当使用多台tomcat时,一定要使用non-sticky模式):

   A:使用默认的sticky session,kryo序列化方式,memcached缓存,Tomcat支持多种序列化策略[java默认序列化tomcat配置/javolution序列化/xstream序列化/flexjson序列化/kryo序列化]

官网介绍说 使用kryo 序列化tomcat的效率最高 所以这里只介绍kryo序列化。具体效率对比,还需要进一步验证。

在context.xml中配置。

<Manager
      className= "de.javakaffee.web.msm.MemcachedBackupSessionManager"
      memcachedNodes= "n1:127.0.0.1:11211"
      sticky="false"
      lockingMode="auto"
      requestUriIgnorePattern= ".*\.(png|gif|jpg|css|js)$" 
      sessionBackupAsync= "false"
      memcachedProtocol="binary"
      copyCollectionsForSerialization="true"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
 />

Manager 各参数说明:

 className

必选项,可配置为

 de.javakaffee.web.msm.MemcachedBackupSessionManager和de.javakaffee.web.msm.DummyMemcachedBackupSessionManager。

其中DummyMemcachedBackupSessionManager可用于测试环境,不需要真实存在memcached。

memcachedNodes

     必选项,memcached的节点信息,格式如:

memcachedNodes="n1:app01:11211,n2:app02:11211"。

failoverNodes

     可选项,不能使用在non-sticky sessions模式。故障转移配置节点,多个使用空格或逗号分开,配置某个节点为备份节点,当其他节点都不可用时才会存储到备

份节点,官方建议配置为和tomcat同服务器的节点。

理由如下:

     假如有两台服务器m1,m2,其中m1部署tomcat和memcached节点n1,m2部署memcached节点n2。

如果配置tomcat的failoverNodes值为n2或者不配置,则当服务器m1挂掉后n1和tomcat中保存的session会丢失,而n2中未保存或者只保存了部分session,这就造成

部分用户状态丢失。

     如果配置tomcat的failoverNodes值为n1,则当m1挂掉后因为n2中保存了所有的session,所以重启tomcat的时候用户状态不会丢失。

为什么n2中保存了所有的session? 因为failoverNodes配置的值是n1,只有当n2节点不可用时才会把session存储到n1,所以这个时候n1中是没有保存任何session的。

username

   可选项,SASL的用户名,如果节点保护membase的bucket uri。

password

   可选项,和username搭配使用。

memcachedProtocol

    可选项,默认text,memcached使用的协议,可选值:text,binary。

    但是我在配置过程中memcachedProtocol="binary"(采用二进制机制) 每次刷新Session都会失效,去掉了就没有问题了,默认是text 文本协议。没找到答案,不知网友有没有遇到过。

sticky

   可选项,默认true,制定是否使用粘性session模式。

lockingMode

   可选值,默认none,只对non-sticky有效。

requestUriIgnorePattern

    可选值,制定忽略那些请求的session操作,一般制定静态资源如css,js一类的。

sessionBackupAsync

    可选值,默认true,是否异步的方式存储到memcached。

backupThreadCount

   可选项,默认是cpu核心数,异步存储session的线程数。

sessionBackupTimeout

    可选项,默认100毫秒,异步存储session的超时时间。

operationTimeout

    可选项,默认1000毫秒,memcached的操作超时时间。

storageKeyPrefix

    可选值,默认值webappVersion,存储到memcached的前缀,主要是为了区分多个webapp共享session的情况。可选值:静态字符串、host、context、webappVersion,

多个使用逗号分割。

sessionAttributeFilter

    可选值,通过正则表达式确定那些session中的属性应该被存储到memcached。例子如:sessionAttributeFilter="^(userName|sessionHistory)$"。

transcoderFactoryClass

    可选值,默认值de.javakaffee.web.msm.JavaSerializationTranscoderFactory,制定序列化和反序列化数据到memcached的工厂类。

在使用kryo Serialization 会报如下错误:

blob.png

解决方式是:

自定义一个序列化 session中的ConcurrentHashMap;

<Context path="/mobilemail" docBase="D:\webapp\WebRoot" reloadable="true">
    <Manager
    className= "de.javakaffee.web.msm.MemcachedBackupSessionManager"
    memcachedNodes= "n1:127.0.0.1:11211"
    sticky="false"
    lockingMode="auto"
    requestUriIgnorePattern= ".*\.(png|gif|jpg|css|js)$" 
    sessionBackupAsync= "false"
    copyCollectionsForSerialization="true"
    transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory" 
    customConverter="org.claros.commons.CustomKryoRegistration"
     />
</Context>

  customConverter配置的com.test.serializer.CustomKryoRegistration这个类是自己写的类,是用来注册一些特殊类型的,同时自己负责对这些类型的序列化,可以放在项目里,也可以单独打个jar包,把以后遇到的所有复杂类型都在这里即可。项目里遇到的kryo没有注册到的类就是session里的java.util.concurrent.ConcurrentHashMap这个类,反序列化的时候本来可以做得更高效些,这里只是为了演示,直接用kryo自带的MapSerializer来反序列化

package com.test.serializer;
import java.util.concurrent.ConcurrentHashMap;
 
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.serialize.MapSerializer;
 
import de.javakaffee.web.msm.serializer.kryo.KryoCustomization;
 
public class CustomKryoRegistration implements KryoCustomization {
   public void customize(Kryo kryo) {
      kryo.register(ConcurrentHashMap.class, new MapSerializer(kryo));
   }
}

Memcached具体的安装配置:

2. 具体安装步骤

  wget https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/libevent-2.0.22-stable.tar.gz

 1.由于memcached依赖于libevent,因此需要安装libevent。由于linux系统可能默认已经安装libevent,执行命令:

rpm -qa|grep libevent

查看系统是否带有该安装软件,如果有执行命令:

rpm -e libevent-1.4.13-4.el6.x86_64 –nodeps(由于系统自带的版本旧,忽略依赖删除)

3. 安装libevent命令:

tar zxvf libevent-2.0.22-stable.tar.gz

  cd libevent-2.0.22-stable

  ./configure –prefix=/usr/local/libevent

  make

  make install

至此libevent安装完毕;

4. 安装memcached命令:

tar zxvf memcached-1.4.2.tar.gz

  cd memcached-memcached-1.4.2

        ./configure –prefix=/usr/local/memcached –with-libevent=/usr/local/libevent

make

make install

  至此memcached安装完毕;

启动报错

如果启动时出现“memcached: error while loading shared libraries:libevent-2.0.so.5: cannot open shared object file: No such file or directory”之类的信息,表示memcached 找不到libevent 的位置。所以,请先使用whereis libevent 得到位置,然后连接到memcached 所寻找的路径。

首先查看libevent 在哪里

whereis libevent

  libevent: /usr/local/libevent

然后,再看memcached 从哪里找它

LD_DEBUG=libs /usr/local/memcached -v 

blob.png    

做个软连接

 sudo ln -s /usr/local/libevent/lib/libevent-2.0.so.5 /usr/lib64/libevent-2.0.so.5

再次启动,问题解决。

说明:如果没有带 -u root 的话就会报:
can't run as root without the -u switch
解决:带-u root就行!

简单启动:

memcached -d -m 512 -p 11211 -u root -P /tmp/memcached.pid

     blob.png

blob.png 

<body>
 
   <%
      //HttpSession session = request.getSession(true);
 
      System.out.println(session.getCreationTime());
 
      out.println("<br> SESSION ID:" + session.getId() + "<br>");
 
      out.println("Session serviced by master" + "<br>");
 
      out.println("Session created time is :" + session.getCreationTime()
           + "<br>");
   %>
 
</body>

三 使用 filter 方法存储

这种方法比较推荐,因为它的服务器使用范围比较多,不仅限于 tomcat ,而且实现的原理比较简单容易控制。

可以使用 memcached-session-filter 或者 Spring-session

官方网址:http://code.google.com/p/memcached-session-filter/

官方介绍:解决集群环境下java web容器session共享,使用filter拦截器和memcached实现。在tomcat 6和websphere 8测试通过,现网并发2000,日PV量1100万。

暂不支持session event包括create destory 和 attribute change

东西很不错,体积很小,不过这个东东要和 spring 一起使用,而且要求存储到 memcached 的对象要实现 java 的序列化接口

大家也知道,java 本身的序列化性能也很一般。

将其简单扩展,就可以不再依赖 spring ,并且利用 javolution 实现序列化,缓存的对象不再有限制。

 

发表评论