基于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
。关键在于对权限的分配。
应用程序本身不用修改。
一、 缓存的处理
缓存要使用统一的缓存服务器,集中式缓存。
原先的实现采用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
。关键在于对权限的分配。
应用程序本身不用修改。
相关文章推荐
- 基于memcached的SNA实现
- 基于memcached的SNA实现(1)
- 基于memcached的SNA实现(2)
- 基于memcached的SNA实现
- 基于memcached的SNA实现
- 【原创】基于Memcached 实现用户登录的Demo(附源码)
- 基于memcached for java 实现通用分布式缓存和集群分布式缓存
- 分布式锁 分段锁 基于 memcached redis zookeeper (3种资源模式) 实现
- memcached学习——常用命令+基于java客户端的3种简单实现(二)
- tomcat session server基于memcached的实现
- nginx负载均衡LAMP及基于memcached实现php会话保存
- Tomcat基于MSM+Memcached实现Session共享
- Tomcat基于MSM+Memcached实现Session共享
- 基于memcached的php锁机制实现
- Tomcat基于MSM+Memcached实现Session共享
- Python之基于Memcached在分布式应用程序中实现高速缓存学习笔记
- C++实现基于离散Hopfield神经网络噪声数字的识别_智能计算作业三
- 基于Spark平台的电影推荐系统实现
- 调用博客paip.基于HTML gui界面的javascript JS实现SLEEP。。
- java中基于线程池和反射机制实现定时任务