系統(tǒng)要集群,使用SNA方案。
一、 緩存的處理
緩存要使用統(tǒng)一的緩存服務(wù)器,集中式緩存。
原先的實現(xiàn)采用ehcache。
在spring里的配置,以資源緩存為例:
<!-- EhCache Manager -->
<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負(fù)責(zé)對ehcache進行管理,初始化、啟動、停止。
resourceCacheBackend負(fù)責(zé)實際執(zhí)行緩存操作,put 、get、remove。
resourceCache實現(xiàn)具有業(yè)務(wù)語義的業(yè)務(wù)應(yīng)用層面的緩存操作,內(nèi)部調(diào)用resourceCacheBackend操作。
現(xiàn)在采用memcached。
關(guān)于客戶端,采用文初封裝的客戶端,地址在http://code.google.com/p/memcache-client-forjava/。
使用spring的FactoryBean進行二次封裝。同理:
memcachedManager負(fù)責(zé)對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();
}
}
配置:
<bean id="memcachedManager"
class="com.framework.extcomponent.cache.MemcachedCacheManagerFactoryBean"/>
resourceCacheBackend負(fù)責(zé)實際執(zhí)行緩存操作,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);
}
}
配置:
<bean id="resourceCacheBackend"
class="com.framework.extcomponent.cache.MemcachedCacheFactoryBean">
<property name="cacheManager" ref="memcachedManager"/>
<property name="cacheName" value="memcache"/>
</bean>
resourceCache同上,替換新的實現(xiàn)類MemcachedBasedResourceCache即可。
二、 Session失效的處理
采用memcached作為httpsession的存儲,并不直接保存httpsession對象,自定義SessionMap,SessionMap直接繼承HashMap,保存SessionMap。
會話膠粘:未失敗轉(zhuǎn)發(fā)的情況下沒必要在memcached保存的SessionMap和httpsession之間復(fù)制來復(fù)制去,眉來眼去。
利用memcached計數(shù)器保存在線人數(shù)。
系統(tǒng)權(quán)限采用了acegi,在acegi的攔截器鏈里配置snaFilter
<bean id="filterChainProxy"
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的職責(zé):
1、 沒有HttpSession時,創(chuàng)建HttpSession;
2、 創(chuàng)建Cookie保存HttpSession id;
3、 如果Cookie保存的HttpSession id與當(dāng)前HttpSession id一致,說明是正常請求;
4、 如果Cookie保存的HttpSession id與當(dāng)前HttpSession id不一致,說明是失敗轉(zhuǎn)發(fā);失敗轉(zhuǎn)發(fā)的處理:
4.1、根據(jù)Cookie保存的HttpSession id從memcached獲取SessionMap;
4.2、SessionMap屬性復(fù)制到當(dāng)前HttpSession;
4.3、memcached刪除SessionMap。
5、 判斷當(dāng)前請求url是否是登出url,是則刪除SessionMap,在線人數(shù)減1.
代碼:
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
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);
//在線人數(shù)減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監(jiān)聽httpsession的屬性變化,同步到memecached中的sessionmap。
public void attributeAdded(HttpSessionBindingEvent event) {
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){
//在線人數(shù)加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);
}
利用HttpSessionListener,sessionDestroyed事件時根據(jù)sessionid刪除memcached里的sessionMap(如果存在)。不再擔(dān)心httpsession的過期問題。
public void sessionDestroyed(HttpSessionEvent 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);
//在線人數(shù)減1
getCache().addOrDecr("userCount",1);
getCache().remove(sessionId);
}
}
三、 文件保存的處理
和緩存類似,采用集中式的文件服務(wù)。對于linux,采用nfs。參考文檔http://linux.vbird.org/linux_server/0330nfs.php#What_NFS_perm。關(guān)鍵在于對權(quán)限的分配。
應(yīng)用程序本身不用修改。
http://www.tkk7.com/ronghao 榮浩原創(chuàng),轉(zhuǎn)載請注明出處:)
posted on 2008-10-28 20:41
ronghao 閱讀(2506)
評論(3) 編輯 收藏 所屬分類:
工作日志