本来一个Tomcat集群4台服务器工作的很好,随着访问量的增加本来的粘性Session方式的配置没办法很好的一碗水端平了.Session复制的话对于网络又是一个负担,所以才自己实现了一套利用Memcache的Session实现.
网上已经有很多这样的实现了,比如修改Tomcat的Manager的http://code.google.com/p/memcached-session-manager/.
但由于我这里还有其他的Servlet容器,所以也就没有使用完全自己实现了一套,现在运行在www.etnet.com.cn网站上.
从那开始呢....其他的Servlet容器我总不能再一一去实现吧。。。。最后还是决定使用过滤器来偷梁换柱了.
先是 CacheSessionFilter ,这个过滤器负责将普通的HttpServletRequest替换成我们自己的实现.
public class CacheSessionFilter extends BaseFilter {
**
* 替换原始的Request,修改为
* com.etnetchina.servlet.wrapper.CacheSessionHttpServletReqeust。
* 并根据是否新生成了Session来更新客户端的cookie.
*
* @param request 请求。
* @param response 响应。
* @param chain 下一个过滤器。
* @throws java.io.IOException
* @throws javax.servlet.ServletException
*/
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
logger.debugLog("CacheSessionFilter to work.");
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
CacheSessionHttpServletReqeust cacheRequest =
new CacheSessionHttpServletReqeust(
httpRequest,
httpResponse,
filterConfig.getServletContext());
cacheRequest.setSessionCookieName(sessionCookieName);
cacheRequest.setMaxInactiveInterval(maxInactiveInterval);
cacheRequest.setCookieDomain(cookieDomain);
cacheRequest.setCookieContextPath(cookieContextPath);
cacheRequest.setSessionAttributeListeners(sessionAttributeListeners);
cacheRequest.setSessionListeners(sessionListeners);
chain.doFilter(cacheRequest, httpResponse);
CacheHttpSession cacheSession = cacheRequest.currentSession();
if (cacheSession != null) {
if (!cacheSession.synchronizationCache()) {
WebUtil.failureCookie(
httpRequest,
httpResponse,
sessionCookieName,
cookieDomain,
cookieContextPath);
}
}
}
}
这个过滤器的核心就是doFilter方法了,做了三件事.
第一是替换HttpServletRequest.
第二是如果Session失效,负责删除Cookie中的SessionId.
最后一件就是如果Session中的数据被改变了同步到缓存中.
现在重点是我们换上的CacheHttpServletRequest有什么用呢,很简单只是在应用调用getSession()方法时返回我们实现的Session.其核心的代码很简单,如下.
private HttpSession doGetSession(boolean create) {
if (cacheSession != null) {
//local,return.
logger.debugLog("Session[{0}] was existed.", cacheSession.getId());
} else {
Cookie cookie = WebUtil.findCookie(this, getSessionCookieName());
if (cookie != null) {
logger.debugLog("Find session`s id from cookie.[{0}]",
cookie.getValue());
cacheSession = buildCacheHttpSession(cookie.getValue(), false);
} else {
cacheSession = buildCacheHttpSession(create);
}
}
if (cacheSession != null) {
//dead?
if (cacheSession.isInvalid()) {
cacheSession.invalidate();
cacheSession.synchronizationCache();
cacheSession = buildCacheHttpSession(create);
}
if (cacheSession != null) {
cacheSession.access();
}
}
return cacheSession;
}
getSession()和getSession(boolean)方法实际调用的就是这个方法,为了减少创建的损耗在一次请求中利保只会创建一次.最后更新一下这个Session的最后访问时间.
每一次请求结束,都会进行一次缓存同步.由于每次讲求都会造成访问时间的更新,所以这个值是一直会被put到缓存中的.
启用只需要在web.xml做如下配置.
修改默认的Session储存机制,改为使用某个缓存来储存。
CacheSessionFilter
com.etnetchina.servlet.filter.session.CacheSessionFilter
sessionId在Cookie中的名称
sessionCookieName
etnetsessionid
Session的最大不活动时间(秒)
maxInactiveInterval
60
还可以有以下参数可配置
cookieDomain为存放cookie的域设置。默认为null.
cookieContextPath为存放cookie的路径。如果不设置将使用默认的contextPath.
sessionAttributeListeners 为HttpSessionAttributeListener监听器实现类全限定名,多个名称以","分隔.
sessionListeners 为HttpSessionListener监听器实现类的全限定名,多个名称以","分隔.
CacheSessionFilter
/*
加上一个过滤器就行了.
最后我提供上完整的源代码,有兴趣的可以提提自己的建议.代码是提供了OSCache和XMemCached的包装实现.
上传的压缩包是一个Netbeans的项目,如果你用的是Eclipse那么直接导入就可以了.