您的位置:首页 > 数据库 > Memcache

基于memcached的SNA实现

2011-05-05 18:14 393 查看
系统要集群,使用SNA方案。

一、 缓存的处理

缓存要使用统一的缓存服务器,集中式缓存。

原先的实现采用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负责对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();

}

}

配置:

<
bean

id
=
"memcachedManager"

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);

}

}

配置:

<
bean

id
=
"resourceCacheBackend"

class
=
"com.framework.extcomponent.cache.MemcachedCacheFactoryBean"
>

<
property

name
=
"cacheManager"

ref
=
"memcachedManager"
/>

<
property

name
=
"cacheName"

value
=
"memcache"
/>

</
bean
>

resourceCache同上,替换新的实现类MemcachedBasedResourceCache即可。

二、 Session失效的处理

采用memcached作为httpsession的存储,并不直接保存httpsession对象,自定义SessionMap,SessionMap直接继承HashMap,保存SessionMap。

会话胶粘:未失败转发的情况下没必要在memcached保存的SessionMap和httpsession之间复制来复制去,眉来眼去。

利用memcached计数器保存在线人数。

系统权限采用了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的职责:

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.

代码:

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);

//在线人数减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。

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
){

//在线人数加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事件时根据sessionid删除memcached里的sessionMap(如果存在)。不再担心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);

//在线人数减1

getCache().addOrDecr(
"userCount"
,
1
);

getCache().remove(sessionId);

}

}

三、 文件保存的处理

和缓存类似,采用集中式的文件服务。对于linux,采用nfs。参考文档http://linux.vbird.org/linux_server/0330nfs.php#What_NFS_perm
。关键在于对权限的分配。

应用程序本身不用修改。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: