采用Memcached实现分布式Session

简介:  memcached-session-manager是一个开源的高可用的Tomcat session共享解决方案,它支持Sticky模式和Non-Sticky模式。Sticky模式表示每次请求都会被映射到同一台后端Web服务器,知道该Web服务器宕机,这样session可先存放在服务器本地,等到请求处理完成再同步到后端memcached服务器;而当Web服务器宕机时,请求被映射到其他Web服务器,这时候,其他Web服务器可以从后端memcache中恢复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>

通过浏览器访问:http://10.10.195.188:8080/test.jsp
可以看到页面显示:

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. 《大型分布式网站架构设计与实践》陈康贤著

相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
1月前
|
存储 缓存 负载均衡
分布式系统Session一致性问题
分布式系统Session一致性问题
31 0
|
4月前
|
负载均衡 算法 NoSQL
聊聊分布式应用中负载均衡技术和Session一致性
聊聊分布式应用中负载均衡技术和Session一致性
37 0
|
5月前
|
负载均衡 算法 NoSQL
分布式系列教程(15) - 解决分布式Session一致性问题
分布式系列教程(15) - 解决分布式Session一致性问题
54 0
|
7月前
|
存储 缓存 NoSQL
Shiro 解决分布式 Session
在分布式系统中,会话管理是一个重要的问题。Shiro框架提供了一种解决方案,通过其会话管理组件来处理分布式会话。本文演示通过RedisSessionManager解决分布式会话问题。
53 0
|
7月前
|
存储 NoSQL Java
Spring Session分布式会话管理
Spring Session分布式会话管理
50 0
|
5月前
|
负载均衡 NoSQL Java
淘东电商项目(30) -解决分布式Session共享问题
淘东电商项目(30) -解决分布式Session共享问题
37 0
|
5月前
|
NoSQL 应用服务中间件 Redis
面试官:分布式环境下,如何实现session共享
随着互联网公司的项目在微服务和分布式的环境下进行的搭建,导致一个项目可能分别部署在几个甚至很多的服务器集群下,此时就会出现一个问题:
|
6月前
|
存储 NoSQL Java
场景应用:利用Redis实现分布式Session
场景应用:利用Redis实现分布式Session
349 0
|
10月前
|
存储 负载均衡 算法
分布式session
1.什么是session HTTP是无状态的,session是一种会话保持技术,目的就是以一种方式来记录http请求之间需要传递、交互的数据。 不是每次http请求都会产生的新的session,而是在服务端手动创建的,例如HttpServletRequest.getSession(true)。 session创建后会返回给客户端sessionID,sessionID会以cookie的形式携带在后续的请求中,直到浏览器关闭,一次会话结束。
38 0
|
11月前
|
存储 JSON NoSQL
Spring Session - 使用Spring Session从零到一构建分布式session
Spring Session - 使用Spring Session从零到一构建分布式session
148 0

热门文章

最新文章