今天学到了新东西, 赶紧记下.
-------------------------------------------------
公司原来有一个项目, 部署在一台tomcat服务器上, 另外还有一个memcached服务器做缓存. 但随着客户越来越多, 访问量越来越大, 发现一台服务器不够用, 于是决定把它部署到阿里云上(两台tomcat), 利用阿里云的SLB实现负载均衡.
部署的过程很顺利, 但在测试中发现一个问题: 当用户通过手机来访问我们的应用时, 如果用户登录之后, 再转换wifi连接, 或者不使用wifi, 直接使用网络流量, 那么当他再访问的时候, 可能会回重定向到登录页面, 要求用户重新登录...
原因是:
1) 项目里把会员的信息都存放在tomcat的session里面
2) 我们利用的是阿里云的基于IP的负载均衡策略
所以, 当客户登录后 (假设处理登录请求的是TomcatA服务器, 登录成功后把客户信息存放在TomcatA的session里), 然后转换wifi连接 (这时他手机的IP改变了), 然后再接着访问的话, 阿里云的负载均衡服务器就会因为请求的IP不同而把这个请求转发给另一个服务器处理 (假设是TomcatB服务器). 然而在TomcatB的session没找到这个客户的信息, 所以会重定向到登录页面.
解决的办法:
1) 首先想到一种方法, 就是不把客户信息放在session里, 而是放在缓存服务器上. 后面要拿客户信息的时候就直接从缓存拿, 而不从session里面拿. 这样不管负载均衡服务器把请求分发给哪个tomcat, 都能拿到客户信息. 但这样有一个弊端, 就是要改代码... 项目里有几十个地方写了"request.getSession().setAttribute(xxx)" 和 "request.getSession().getAttribute(xxx)" , 要一个一个改的话, 首先要弄清楚为什么要把这个变量放进session, 然后弄清楚其他地方是不是一定要从session里面拿. 很麻烦..
2) 后来发现了一种简单的方法, 不用改代码, 只需导入几个包, 再加一个配置就可以解决问题. 那就是使用memcached-session-manager. 它可以通过配置, 把session存放在缓存服务器上, 而不是tomcat服务器. 这样, 在代码里仍然可以把客户信息放在session里, 后面要用到的时候, request.getSession() 就会从缓存服务器里拿session, 而不是从tomcat里拿, 从而保证不管哪个tomcat服务器处理的请求, 都可以拿到客户对应的session.
具体的方法:
1) 把下面四个包放进tomcat的lib里:
couchbase-client-1.2.2.jar
memcached-session-manager-1.6.5.jar
memcached-session-manager-tc6-1.6.3.jar
spymemcached-2.10.2.jar
2) 在tomcat的server.xml里加上下面配置:
<Context docBase="xxx" path="/xxx" reloadable="true" source="org.eclipse.jst.jee.server:xxx"> <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:192.168.1.55:11211" sticky="false" sessionBackupAsync="false" lockingMode="auto" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.JavaSerializationTranscoderFactory"/> </Context>
有几个地方要注意:
1) 这些包要放在tomcat的lib里, 不是项目的lib, 不然tomcat会启动失败.
2) 注意这些包的版本要和对应的tomcat版本搭配 (tomcat6用这几个包没问题)
再具体的方法不详细说明了, 推荐下面几条连接, 说的比较详细:
http://www.cnblogs.com/phirothing/archive/2013/12/05/3459814.html
http://blog.csdn.net/a__java___a/article/details/8738932