系统要集群,使用SNA方案。
一、 缓存的处理
缓存要使用统一的缓存服务器,集中式缓存。
原先的实现采用ehcache。
在spring里的配置,以资源缓存为例:
< bean id ="cacheManager" class ="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" >
< property name ="configLocation" >
< value > classpath:ehcache.xml </ value >
</ property >
</ bean >
< bean id ="resourceCacheBackend"
class ="org.springframework.cache.ehcache.EhCacheFactoryBean" >
< property name ="cacheManager" ref ="cacheManager" />
< property name ="cacheName" value ="resourceCache" />
</ bean >
< bean id ="resourceCache"
class ="com.framework.extcomponent.security.authentication.services.acegi.cache.EhCacheBasedResourceCache"
autowire ="byName" >
< property name ="cache" ref ="resourceCacheBackend" />
</ bean >
cacheManager负责对ehcache进行管理,初始化、启动、停止。
resourceCacheBackend负责实际执行缓存操作,put 、get、remove。
resourceCache实现具有业务语义的业务应用层面的缓存操作,内部调用resourceCacheBackend操作。
现在采用memcached。
关于客户端,采用文初封装的客户端,地址在http://code.google.com/p/memcache-client-forjava/。
使用spring的FactoryBean进行二次封装。同理:
memcachedManager负责对memcached进行管理,初始化、启动、停止。
代码:
* User: ronghao
* Date: 2008-10-14
* Time: 10:36:30
* 管理Memcached 的CacheManager
*/
public class MemcachedCacheManagerFactoryBean implements FactoryBean, InitializingBean, DisposableBean {
protected final Log logger = LogFactory.getLog(getClass());
private ICacheManager < IMemcachedCache > cacheManager;
public Object getObject() throws Exception {
return cacheManager;
}
public Class getObjectType() {
return this .cacheManager.getClass();
}
public boolean isSingleton() {
return true ;
}
public void afterPropertiesSet() throws Exception {
logger.info( " Initializing Memcached CacheManager " );
cacheManager = CacheUtil.getCacheManager(IMemcachedCache. class ,
MemcachedCacheManager. class .getName());
cacheManager.start();
}
public void destroy() throws Exception {
logger.info( " Shutting down Memcached CacheManager " );
cacheManager.stop();
}
}
配置:
class ="com.framework.extcomponent.cache.MemcachedCacheManagerFactoryBean" />
resourceCacheBackend负责实际执行缓存操作,put 、get、remove。
代码:
* User: ronghao
* Date: 2008-10-14
* Time: 10:37:16
* 返回 MemcachedCache
*/
public class MemcachedCacheFactoryBean implements FactoryBean, BeanNameAware, InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private ICacheManager < IMemcachedCache > cacheManager;
private String cacheName;
private String beanName;
private IMemcachedCache cache;
public void setCacheManager(ICacheManager < IMemcachedCache > cacheManager) {
this .cacheManager = cacheManager;
}
public void setCacheName(String cacheName) {
this .cacheName = cacheName;
}
public Object getObject() throws Exception {
return cache;
}
public Class getObjectType() {
return this .cache.getClass();
}
public boolean isSingleton() {
return true ;
}
public void setBeanName(String name) {
this .beanName = name;
}
public void afterPropertiesSet() throws Exception {
// If no cache name given, use bean name as cache name.
if ( this .cacheName == null ) {
this .cacheName = this .beanName;
}
cache = cacheManager.getCache(cacheName);
}
}
配置:
class ="com.framework.extcomponent.cache.MemcachedCacheFactoryBean" >
< property name ="cacheManager" ref ="memcachedManager" />
< property name ="cacheName" value ="memcache" />
</ bean >
二、 Session失效的处理
采用memcached作为httpsession的存储,并不直接保存httpsession对象,自定义SessionMap,SessionMap直接继承HashMap,保存SessionMap。
会话胶粘:未失败转发的情况下没必要在memcached保存的SessionMap和httpsession之间复制来复制去,眉来眼去。
利用memcached计数器保存在线人数。
系统权限采用了acegi,在acegi的拦截器链里配置snaFilter
class ="org.acegisecurity.util.FilterChainProxy" >
< property name ="filterInvocationDefinitionSource" >
< value >
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=snaFilter,httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor
</ value >
</ property >
</ bean >
注意需要配置在第一个。
snaFilter的职责:
1、 没有HttpSession时,创建HttpSession;
2、 创建Cookie保存HttpSession id;
3、 如果Cookie保存的HttpSession id与当前HttpSession id一致,说明是正常请求;
4、 如果Cookie保存的HttpSession id与当前HttpSession id不一致,说明是失败转发;失败转发的处理:
4.1、根据Cookie保存的HttpSession id从memcached获取SessionMap;
4.2、SessionMap属性复制到当前HttpSession;
4.3、memcached删除SessionMap。
5、 判断当前请求url是否是登出url,是则删除SessionMap,在线人数减1.
代码:
FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest hrequest = (HttpServletRequest) servletRequest;
final HttpServletResponse hresponse = (HttpServletResponse) servletResponse;
String uri = hrequest.getRequestURI();
logger.debug( " 开始SNA拦截----------------- " + uri);
HttpSession httpSession = hrequest.getSession();
String sessionId = httpSession.getId();
// 如果是登出,则直接干掉sessionMap
if (uri.equals(logoutUrl)) {
logger.debug( " remove sessionmap: " + sessionId);
// 在线人数减1
getCache().addOrDecr( " userCount " , 1 );
getCache().remove(sessionId);
} else {
String cookiesessionid = getSessionIdFromCookie(hrequest, hresponse);
if ( ! sessionId.equals(cookiesessionid)) {
createCookie(sessionId, hresponse);
SessionMap sessionMap = getSessionMap(cookiesessionid);
if (sessionMap != null ) {
logger.debug( " fail over--------sessionid: " + sessionId + " cookiesessionid: " + cookiesessionid);
initialHttpSession(sessionMap, httpSession);
cache.remove(cookiesessionid);
}
}
}
filterChain.doFilter(hrequest, hresponse);
}
利用HttpSessionAttributeListener监听httpsession的属性变化,同步到memecached中的sessionmap。
HttpSession httpSession = event.getSession();
String attrName = event.getName();
Object attrValue = event.getValue();
String sessionId = httpSession.getId();
logger.debug( " attributeAdded sessionId: " + sessionId + " name: " + attrName + " ,value: " + attrValue);
SessionMap sessionMap = getSessionMap(sessionId);
if (sessionMap == null ){
// 在线人数加1
getCache().addOrIncr( " userCount " , 1 );
sessionMap = new SessionMap();
}
logger.debug( " name: " + attrName + " ,value: " + attrValue);
sessionMap.put(attrName, attrValue);
getCache().put(sessionId, sessionMap);
}
public void attributeRemoved(HttpSessionBindingEvent event) {
HttpSession httpSession = event.getSession();
String attrName = event.getName();
String sessionId = httpSession.getId();
logger.debug( " attributeRemoved sessionId: " + sessionId + " name: " + attrName);
SessionMap sessionMap = getSessionMap(sessionId);
if (sessionMap != null ) {
logger.debug( " remove: " + attrName);
sessionMap.remove(attrName);
getCache().put(sessionId, sessionMap);
}
}
public void attributeReplaced(HttpSessionBindingEvent event) {
attributeAdded(event);
}
HttpSession httpSession = event.getSession();
String sessionId = httpSession.getId();
logger.debug( " session Removed sessionId: " + sessionId);
SessionMap sessionMap = getSessionMap(sessionId);
if (sessionMap != null ) {
logger.debug( " remove sessionmap: " + sessionId);
// 在线人数减1
getCache().addOrDecr( " userCount " , 1 );
getCache().remove(sessionId);
}
}
三、 文件保存的处理
和缓存类似,采用集中式的文件服务。对于linux,采用nfs。参考文档http://linux.vbird.org/linux_server/0330nfs.php#What_NFS_perm。关键在于对权限的分配。
应用程序本身不用修改。
http://www.blogjava.net/ronghao 荣浩原创,转载请注明出处:)