上篇文章中的方案,在外部显式的使用memcache来替代session,虽然可以达到各个服务器session共享的目的,但是改变了开发人员获取session的方式。
本篇介绍的方法可以在不重构原来代码,不改变代码习惯的情况下,实现session共享的目的。
两种方法各有利弊,第一种方法编码方式改变比较大,开发人员可能不习惯,但是不受服务器类型的影响;第二种方法,针对tomcat服务器,需要修改tomcat源码,但是不需要重构代码,代码中对session的操作还和以前一样。
第二种方式主要做两处改动:
1,使用memcache来覆盖tomcat对session的实现。将session存入memcache中。
package org.apache.catalina.session; import java.io.*; import com.danga.MemCached.*; import org.apache.catalina.*; public class MemcachedManager extends StandardManager { protected MemCachedClient mc = null; protected SockIOPool pool = null; protected String sockPoolName = "snasessionsock"; protected String serverlist = "192.168.2.194:12000"; protected String snaidPerfix = "snaid"; protected String snaidFlag = "true"; public MemcachedManager() { super(); } public Session findSession(String id) throws IOException { Session session = super.findSession(id); if (session == null && id != null) { try { Object sid = mc.get(this.getSnaidPerfix() + id); if (sid != null) { session = createSession(id); } } catch (Exception ex) { ex.printStackTrace(); } } return session; } public Session createSession(String sessionId) { Session session = super.createSession(sessionId); mc.set(this.getSnaidPerfix() + session.getId(), snaidFlag); return session; } protected StandardSession getNewSession() { return new MemcachedSession(this, mc); } protected void initPool() { try { if (pool == null) { try { pool = SockIOPool.getInstance(); pool.setServers(serverlist.split(",")); pool.setInitConn(5); pool.setMinConn(5); pool.setMaxConn(50); pool.setMaintSleep(30); pool.setNagle(false); pool.initialize(); } catch (Exception ex) { // log.error("error:", ex); } } } catch (Exception ex) { // log.error("error:", ex); } if (mc == null) { mc = new MemCachedClient(); // mc.setPoolName(sockPoolName); mc.setCompressEnable(false); mc.setCompressThreshold(0); } } protected void closePool() { if (mc != null) { try { } catch (Exception ex) { // log.error("error:", ex); } mc = null; } if (pool != null) { try { pool.shutDown(); } catch (Exception ex) { // log.error("error:", ex); } } } public String getSockPoolName() { return sockPoolName; } public String getServerlist() { return serverlist; } public String getSnaidPerfix() { return snaidPerfix; } public String getSnaidFlag() { return snaidFlag; } public void setSockPoolName(String sockPoolName) { this.sockPoolName = sockPoolName; } public void setServerlist(String serverlist) { this.serverlist = serverlist; } public void setSnaidPerfix(String snaidPerfix) { this.snaidPerfix = snaidPerfix; } public void setSnaidFlag(String snaidFlag) { this.snaidFlag = snaidFlag; } protected String generateSessionId() { if (this.getJvmRoute() != null) { return java.util.UUID.randomUUID().toString() + '.' + this.getJvmRoute(); } return java.util.UUID.randomUUID().toString(); } public void start() throws LifecycleException { this.setPathname(""); // must disable session persistence across Tomcat restarts super.start(); this.initPool(); } public void stop() throws LifecycleException { super.stop(); this.closePool(); } }
---------------------------------------------------------------------------------------------------
package org.apache.catalina.session; import com.danga.MemCached.*; import org.apache.catalina.*; public class MemcachedSession extends StandardSession { protected transient MemCachedClient mc = null; public MemcachedSession(Manager manager, MemCachedClient mc) { super(manager); this.mc = mc; } public Object getAttribute(String name) { Object obj = super.getAttribute(name); if (obj != null && !(obj instanceof java.io.Serializable)) { return obj; } String key = name + this.getId(); obj = mc.get(key); return obj; } public void setAttribute(String name, Object value) { removeAttribute(name); super.setAttribute(name, value); if (value != null && value instanceof java.io.Serializable) { String key = name + this.getId(); mc.set(key, value); } } protected void removeAttributeInternal(String name, boolean notify) { super.removeAttributeInternal(name, notify); String key = name + this.getId(); mc.delete(key); } public void expire(boolean notify) { mc.delete(((MemcachedManager) manager).getSnaidPerfix() + this.getId()); super.expire(notify); } }
(可以使用自己项目中memcache的操作类来替代上面两个类的memcache操作)
将两个类打jar包,置于tomcat/lib下
修改tomcat/conf/context.xml文件,内容如下:
<?xml version='1.0' encoding='utf-8'?> <Context> <Loader delegate="true"/> <!--这句话需要添加,不加可能报错--> <!-- Default set of monitored resources --> <WatchedResource>WEB-INF/web.xml</WatchedResource> <Manager className="org.apache.catalina.session.MemcachedManager" serverlist="192.168.2.194:12000" snaidPerfix="snaid" snaidFlag="true"> </Manager> </Context>
经过以上的修改,tomcat对session的操作会自动使用org.apache.catalina.session.MemcachedManager 来替代。
2,修改tomcat源码,使tomcat在向客户端setcookie的时候,设置cookie的作用域为全站。这样可以所有的tomcat共用一个JSESSIONID。
下载tomcat的源码。
修改org.apache.catalina.connector.Request.java
/** * Configures the given JSESSIONID cookie. * * @param cookie The JSESSIONID cookie to be configured */ protected void configureSessionCookie(Cookie cookie) { cookie.setMaxAge(-1); String contextPath = null; if (!connector.getEmptySessionPath() && (getContext() != null)) { contextPath = getContext().getEncodedPath(); } if ((contextPath != null) && (contextPath.length() > 0)) { cookie.setPath(contextPath); } else { cookie.setPath("/"); } //added by sufeng,make tomcat jsessionid cross docin.com if (null != System.getProperty("sessionShareDomain") && !"".equals(System.getProperty("sessionShareDomain"))) cookie.setDomain(System.getProperty("sessionShareDomain")); //added by sufeng end if (isSecure()) { cookie.setSecure(true); } }
- sessionShareDomain=sufeng.com
- 重新访问,会发现,JSEESIONID的作用域变成了sufeng.com,这样在两个子域的tomcat服务器可以共享同一个sessionid,并通过相同的sessionid到memcache中取session数据。不对tomcat源码进行修改,两个二级域的tomcat将分别生成不同的JSESSION值,并且作用域是自己的二级域名。即图中上面的两个cookie。
- 参考:
- http://www.javayou.com/diary/8534
- http://www.javaeye.com/topic/81641
- http://soyul.javaeye.com/blog/445838
- http://apache.freelamp.com/tomcat/tomcat-6/v6.0.20/src/apache-tomcat-6.0.20-src.zip