采用Memcached实现分布式Session

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介:  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. 《大型分布式网站架构设计与实践》陈康贤著

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
6月前
|
存储 负载均衡 NoSQL
分布式Session
分布式Session
56 0
|
6月前
|
存储 缓存 负载均衡
分布式系统Session一致性问题
分布式系统Session一致性问题
82 0
|
6月前
|
负载均衡 算法 NoSQL
聊聊分布式应用中负载均衡技术和Session一致性
聊聊分布式应用中负载均衡技术和Session一致性
77 0
|
负载均衡 算法 NoSQL
分布式系列教程(15) - 解决分布式Session一致性问题
分布式系列教程(15) - 解决分布式Session一致性问题
133 0
|
存储 缓存 NoSQL
Shiro 解决分布式 Session
在分布式系统中,会话管理是一个重要的问题。Shiro框架提供了一种解决方案,通过其会话管理组件来处理分布式会话。本文演示通过RedisSessionManager解决分布式会话问题。
103 0
|
存储 NoSQL Java
Spring Session分布式会话管理
Spring Session分布式会话管理
97 0
|
5月前
|
NoSQL Java 应用服务中间件
大厂面试必备:如何轻松实现分布式Session管理?
这篇文章介绍三种分布式Session的实现方案:基于JWT的Token、基于Tomcat的Redis和基于Spring的Redis。JWT方案通过生成Token存储用户信息,实现无状态、可扩展的会话管理,但可能增加请求负载且数据安全性较低。Tomcat与Redis结合,通过配置Tomcat和Redis,实现Session集中管理和高性能存储,但配置相对复杂。Spring整合Redis适用于SpringBoot和SpringCloud项目,集成方便,扩展性强,但同样依赖外部Redis服务。每种方法有其优缺点,适用场景不同。作者小米是一个技术爱好者,欢迎关注其微信公众号“软件求生”获取更多技术内容
243 4
|
30天前
|
存储 缓存 NoSQL
分布式架构下 Session 共享的方案
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求、系统架构和性能要求等因素,选择合适的 Session 共享方案。同时,还需要不断地进行优化和调整,以确保系统的稳定性和可靠性。
|
2月前
|
存储 NoSQL Java
使用springSession完成分布式session
本文介绍了如何使用 `spring-session` 实现分布式 Session 管理,并提供了将 Session 存储在 Redis 中的具体配置示例。通过配置相关依赖及 Spring 的配置文件,可以轻松实现 Session 的分布式存储。示例中详细展示了所需的 Maven 依赖、Spring 配置及过滤器配置,并给出了启动项目后在 Redis 中查看 Session 数据的方法。
|
3月前
|
存储 NoSQL Java
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
这篇文章是关于Java面试中的分布式架构问题的笔记,包括分布式架构下的Session共享方案、RPC和RMI的理解、分布式ID生成方案、分布式锁解决方案以及分布式事务解决方案。
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)