采用Memcached实现分布式Session

memcached-session-manager是一个开源的高可用的Tomcat session共享解决方案,它支持Sticky模式和Non-Sticky模式。Sticky模式表示每次请求都会被映射到同一台后端Web服务器,知道该Web服务器宕机,这样session可先存放在服务器本地,等到请求处理完成再同步到后端memcached服务器;而当Web服务器宕机时,请求被映射到其他Web服务器,这时候,其他Web服务器可以从后端memcache中恢复session。对于Non-Sticky模式来说,请求每次映射的后端Web服务器是不确定的,当请求到来时,从memcached中加载session;当请求处理完成时,将session再协会到memcached。

以Non-Sticky模式为例,首先需要安装memcached的服务器,这个在上一篇中已经讲述过了。然后在Tomcat的$CATALINA_HOME/conf/context.xml文件配置SessionManager,具体配置如下:

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

其中,memcachedNodes指定了memcached的节点;sticky表示是否采用sticky模式;sessionBackuoAsync表示是否采用异步方式备份session;lockingMode表示session的锁定模式;auto表示对于只读请求,session将不会被锁定,如果包含写入请求,则session会被锁定;requestUriIgnorePattern表示忽略的url; transcoderFactoryClass用来指定序列化的方式,这里采用的是Kryo序列化,也是memcached-session-manager比较推荐的一种序列化方式。也可以采用其他序列化方式,譬如:javolution-serializer, xstream-serializer, flexjson-serializer。

memcached-session-manager依赖于memcached-session-manager-{version}.jar,如果使用的是tomcat6,则还需要下载memcached-session-manager-tc6-{version}.jar,如果是tomcat7则采用memcached-session-manager-tc7-{version}.jar的包(博主采用的是tomcat7+jdk7)。还需要spymemcached-2.7.3.jar,在启动tomcat之前需要将这些jar包放到tomcat的lib目录下。如果采用Kryo方式序列化,还需要加入其他一些包,所有包如下:
这里写图片描述
注意这些包的版本,搞错一下就不能实现其功能。这里博主已经整理好了,可以在这里下载。


常见错误

1 启动tomcat出现错误案例1

严重: Error manager.start()
org.apache.catalina.LifecycleException: Failed to start component [de.javakaffee.web.msm.MemcachedBackupSessionManager[/Architecture]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5501)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:672)
    at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1859)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NoClassDefFoundError: org/objenesis/strategy/InstantiatorStrategy
    at de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory.getTranscoder(KryoTranscoderFactory.java:64)
    at de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory.createTranscoder(KryoTranscoderFactory.java:47)
    at de.javakaffee.web.msm.MemcachedSessionService.createTranscoderService(MemcachedSessionService.java:493)
    at de.javakaffee.web.msm.MemcachedSessionService.startInternal(MemcachedSessionService.java:448)
    at de.javakaffee.web.msm.MemcachedBackupSessionManager.startInternal(MemcachedBackupSessionManager.java:535)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 12 more
Caused by: java.lang.ClassNotFoundException: org.objenesis.strategy.InstantiatorStrategy
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 18 more

这个是包版本问题,memcached-session-manager以及msm-kryo-serializer版本不对。

2 启动tomcat出现错误案例2

严重: Error manager.start()
org.apache.catalina.LifecycleException: Failed to start component [de.javakaffee.web.msm.MemcachedBackupSessionManager[]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5501)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
    at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1245)
    at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1895)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NoSuchMethodError: de.javakaffee.web.msm.MemcachedSessionService$SessionManager.getContainerClassLoader()Ljava/lang/ClassLoader;
    at de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory.createTranscoder(KryoTranscoderFactory.java:47)
    at de.javakaffee.web.msm.MemcachedSessionService.createTranscoderService(MemcachedSessionService.java:449)
    at de.javakaffee.web.msm.MemcachedSessionService.startInternal(MemcachedSessionService.java:425)
    at de.javakaffee.web.msm.MemcachedBackupSessionManager.startInternal(MemcachedBackupSessionManager.java:509)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 12 more

这个也是包版本问题,msm-kryo-serializer版本不对

3 在context.xml中配置failoverNodes节点,如下

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

启动tomcat报错:

严重: Error manager.start()
org.apache.catalina.LifecycleException: Failed to start component [de.javakaffee.web.msm.MemcachedBackupSessionManager[/Architecture]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5501)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:672)
    at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1859)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: For a single memcached node there should/must no failoverNodes be specified.
    at de.javakaffee.web.msm.MemcachedNodesManager.createFor(MemcachedNodesManager.java:224)
    at de.javakaffee.web.msm.MemcachedSessionService.createMemcachedNodesManager(MemcachedSessionService.java:445)
    at de.javakaffee.web.msm.MemcachedSessionService.startInternal(MemcachedSessionService.java:410)
    at de.javakaffee.web.msm.MemcachedBackupSessionManager.startInternal(MemcachedBackupSessionManager.java:509)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 12 more

可以看到报错中“Caused by: java.lang.IllegalArgumentException: For a single memcached node there should/must no failoverNodes be specified.”这句,需要把failoverNodes去掉。


应用举例

这里博主采用keepalived+lvs进行负载均衡搭建。详细可以参考LVS+Keepalived实现负载均衡和双机热备,不了解也没关系,这个不是本篇的重点。

主机ip:10.10.195.107
备机ip:10.10.195.187
虚拟ip:10.10.195.188

(配的是双机热备+负载均衡,主备机会虚拟出一个访问的ip,当主机宕机时,会自动切换到备机,对于用户而言完全透明。了解即可)

在主备机web路径的根目录下放入test.jsp,代码如下(备机的就把 10.10.195.107换成10.10.195.187):

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %>
<%@ page session="true" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Memcached-session-manager</title>
</head>
<body >
    10.10.195.107
    <%
        out.print("<br> SESSION-ID:"+session.getId()+"<br>");
    %>
</body>
</html>
通过浏览器访问:<a href="http://10.10.195.188:8080/test.jsp">http://10.10.195.188:8080/test.jsp</a>

可以看到页面显示:

10.10.195.107 
SESSION-ID:6F9B1255A1C9E3F7D5FC144DD81217CD-n1

在主机中输入: sudo service keepalived stop之后(切换到备机),页面显示:

10.10.195.187 
SESSION-ID:6F9B1255A1C9E3F7D5FC144DD81217CD-n1

可以看到访问的页面变了,但是session并没有改变,因为它存在memcached服务器中。


参考资料:
1. LVS+Keepalived实现负载均衡和双机热备
2. MSM–Memcached_Session_Manager介绍及使用
3. memcached-session-manager配置
4. 《大型分布式网站架构设计与实践》陈康贤著



相关文章

发表评论

Comment form

(*) 表示必填项

还没有评论。

跳到底部
返回顶部