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

基于memcached的SNA实现

2011-05-05 18:14 92 查看

系统要集群,使用SNA方案。
一、 缓存的处理
缓存要使用统一的缓存服务器,集中式缓存。
原先的实现采用ehcache。
在spring里的配置,以资源缓存为例:

  1. <!--EhCacheManager-->
  2. < bean id = "cacheManager" class = "org.springframework.cache.ehcache.EhCacheManagerFactoryBean" >
  3. < property name = "configLocation" >
  4. < value > classpath:ehcache.xml </ value >
  5. </ property >
  6. </ bean >
  7. < bean id = "resourceCacheBackend"
  8. class = "org.springframework.cache.ehcache.EhCacheFactoryBean" >
  9. < property name = "cacheManager" ref = "cacheManager" />
  10. < property name = "cacheName" value = "resourceCache" />
  11. </ bean >
  12. < bean id = "resourceCache"
  13. class = "com.framework.extcomponent.security.authentication.services.acegi.cache.EhCacheBasedResourceCache"
  14. autowire = "byName" >
  15. < property name = "cache" ref = "resourceCacheBackend" />
  16. </ bean >

cacheManager负责对ehcache进行管理,初始化、启动、停止。
resourceCacheBackend负责实际执行缓存操作,put 、get、remove。
resourceCache实现具有业务语义的业务应用层面的缓存操作,内部调用resourceCacheBackend操作。

现在采用memcached。
关于客户端,采用文初封装的客户端,地址在http://code.google.com/p/memcache-client-forjava/
使用spring的FactoryBean进行二次封装。同理:
memcachedManager负责对memcached进行管理,初始化、启动、停止。
代码:

  1. /**
  2. *User:ronghao
  3. *Date:2008-10-14
  4. *Time:10:36:30
  5. *管理Memcached的CacheManager
  6. */
  7. public class MemcachedCacheManagerFactoryBean implements FactoryBean,InitializingBean,DisposableBean{
  8. protected final Loglogger=LogFactory.getLog(getClass());
  9. private ICacheManager<IMemcachedCache>cacheManager;
  10. public ObjectgetObject() throws Exception{
  11. return cacheManager;
  12. }
  13. public ClassgetObjectType(){
  14. return this .cacheManager.getClass();
  15. }
  16. public boolean isSingleton(){
  17. return true ;
  18. }
  19. public void afterPropertiesSet() throws Exception{
  20. logger.info( "InitializingMemcachedCacheManager" );
  21. cacheManager=CacheUtil.getCacheManager(IMemcachedCache. class ,
  22. MemcachedCacheManager. class .getName());
  23. cacheManager.start();
  24. }
  25. public void destroy() throws Exception{
  26. logger.info( "ShuttingdownMemcachedCacheManager" );
  27. cacheManager.stop();
  28. }
  29. }


配置:

  1. < bean id = "memcachedManager"
  2. class = "com.framework.extcomponent.cache.MemcachedCacheManagerFactoryBean" />


resourceCacheBackend负责实际执行缓存操作,put 、get、remove。
代码:

  1. /**
  2. *User:ronghao
  3. *Date:2008-10-14
  4. *Time:10:37:16
  5. *返回MemcachedCache
  6. */
  7. public class MemcachedCacheFactoryBean implements FactoryBean,BeanNameAware,InitializingBean{
  8. protected final Loglogger=LogFactory.getLog(getClass());
  9. private ICacheManager<IMemcachedCache>cacheManager;
  10. pri 1eedf vate StringcacheName;
  11. private StringbeanName;
  12. private IMemcachedCachecache;
  13. public void setCacheManager(ICacheManager<IMemcachedCache>cacheManager){
  14. this .cacheManager=cacheManager;
  15. }
  16. public void setCacheName(StringcacheName){
  17. this .cacheName=cacheName;
  18. }
  19. public ObjectgetObject() throws Exception{
  20. return cache;
  21. }
  22. public ClassgetObjectType(){
  23. return this .cache.getClass();
  24. }
  25. public boolean isSingleton(){
  26. return true ;
  27. }
  28. public void setBeanName(Stringname){
  29. this .beanName=name;
  30. }
  31. public void afterPropertiesSet() throws Exception{
  32. //Ifnocachenamegiven,usebeannameascachename.
  33. if ( this .cacheName== null ){
  34. this .cacheName= this .beanName;
  35. }
  36. cache=cacheManager.getCache(cacheName);
  37. }
  38. }


配置:

  1. < bean id = "resourceCacheBackend"
  2. class = "com.framework.extcomponent.cache.MemcachedCacheFactoryBean" >
  3. < property name = "cacheManager" ref = "memcachedManager" />
  4. < property name = "cacheName" value = "memcache" />
  5. </ bean >


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

二、 Session失效的处理
采用memcached作为httpsession的存储,并不直接保存httpsession对象,自定义SessionMap,SessionMap直接继承HashMap,保存SessionMap。

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

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

系统权限采用了acegi,在acegi的拦截器链里配置snaFilter

  1. < bean id = "filterChainProxy"
  2. class = "org.acegisecurity.util.FilterChainProxy" >
  3. < property name = "filterInvocationDefinitionSource" >
  4. < value >
  5. CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
  6. PATTERN_TYPE_APACHE_ANT
  7. /**=snaFilter,httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,basicProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor
  8. </ value >
  9. </ property >
  10. </ 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.

代码:

  1. public void doFilter(ServletRequestservletRequest,ServletResponseservletResponse,
  2. FilterChainfilterChain) throws IOException,ServletException{
  3. final HttpServletRequesthrequest=(HttpServletRequest)servletRequest;
  4. final HttpServletResponsehresponse=(HttpServletResponse)servletResponse;
  5. Stringuri=hrequest.getRequestURI();
  6. logger.debug( "开始SNA拦截-----------------" +uri);
  7. HttpSessionhttpSession=hrequest.getSession();
  8. StringsessionId=httpSession.getId();
  9. //如果是登出,则直接干掉sessionMap
  10. if (uri.equals(logoutUrl)){
  11. logger.debug( "removesessionmap:" +sessionId);
  12. //在线人数减1
  13. getCache().addOrDecr( "userCount" , 1 );
  14. getCache().remove(sessionId);
  15. } else {
  16. Stringcookiesessionid=getSessionIdFromCookie(hrequest,hresponse);
  17. if (!sessionId.equals(cookiesessionid)){
  18. createCookie(sessionId,hresponse);
  19. SessionMapsessionMap=getSessionMap(cookiesessionid);
  20. if (sessionMap!= null ){
  21. logger.debug( "failover--------sessionid:" +sessionId+ "cookiesessionid:" +cookiesessionid);
  22. initialHttpSession(sessionMap,httpSession);
  23. cache.remove(cookiesessionid);
  24. }
  25. }
  26. }
  27. filterChain.doFilter(hrequest,hresponse);
  28. }



利用HttpSessionAttributeListener监听httpsession的属性变化,同步到memecached中的sessionmap。

  1. public void attributeAdded(HttpSessionBindingEventevent){
  2. HttpSessionhttpSession=event.getSession();
  3. StringattrName=event.getName();
  4. ObjectattrValue=event.getValue();
  5. StringsessionId=httpSession.getId();
  6. logger.debug( "attributeAddedsessionId:" +sessionId+ "name:" +attrName+ ",value:" +attrValue);
  7. SessionMapsessionMap=getSessionMap(sessionId);
  8. if (sessionMap== null ){
  9. //在线人数加1
  10. getCache().addOrIncr( "userCount" , 1 );
  11. sessionMap= new SessionMap();
  12. }
  13. logger.debug( "name:" +attrName+ ",value:" +attrValue);
  14. sessionMap.put(attrName,attrValue);
  15. getCache().put(sessionId,sessionMap);
  16. }
  17. public void attributeRemoved(HttpSessionBindingEventevent){
  18. HttpSessionhttpSession=event.getSession();
  19. StringattrName=event.getName();
  20. StringsessionId=httpSession.getId();
  21. logger.debug( "attributeRemovedsessionId:" +sessionId+ "name:" +attrName);
  22. SessionMapsessionMap=getSessionMap(sessionId);
  23. if (sessionMap!= null ){
  24. logger.debug( "remove:" +attrName);
  25. sessionMap.remove(attrName);
  26. getCache().put(sessionId,sessionMap);
  27. }
  28. }
  29. public void attributeReplaced(HttpSessionBindingEventevent){
  30. attributeAdded(event);
  31. }



利用HttpSessionListener,sessionDestroyed事件时根据sessionid删除memcached里的sessionMap(如果存在)。不再担心httpsession的过期问题。

  1. public void sessionDestroyed(HttpSessionEventevent){
  2. HttpSessionhttpSession=event.getSession();
  3. StringsessionId=httpSession.getId();
  4. logger.debug( "sessionRemovedsessionId:" +sessionId);
  5. SessionMapsessionMap=getSessionMap(sessionId);
  6. if (sessionMap!= null ){
  7. logger.debug( "removesessionmap:" +sessionId);
  8. //在线人数减1
  9. getCache().addOrDecr( "userCount" , 1 );
  10. getCache().remove(sessionId);
  11. }
  12. }

三、 文件保存的处理
和缓存类似,采用集中式的文件服务。对于linux,采用nfs。参考文档http://linux.vbird.org/linux_server/0330nfs.php#What_NFS_perm 。关键在于对权限的分配。
应用程序本身不用修改。

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