基于memcached的SNA实现

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

  1. <!-- EhCache Manager -->
  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  Log logger = LogFactory.getLog(getClass());
  9.      private  ICacheManager<IMemcachedCache> cacheManager;
  10.      public  Object getObject()  throws  Exception {
  11.          return  cacheManager;
  12.     }
  13.      public  Class getObjectType() {
  14.          return   this .cacheManager.getClass();
  15.     }
  16.      public   boolean  isSingleton() {
  17.          return   true ;
  18.     }
  19.      public   void  afterPropertiesSet()  throws  Exception {
  20.         logger.info( "Initializing Memcached CacheManager" );
  21.         cacheManager = CacheUtil.getCacheManager(IMemcachedCache. class ,
  22.                 MemcachedCacheManager. class .getName());
  23.         cacheManager.start();
  24.     }
  25.      public   void  destroy()  throws  Exception {
  26.         logger.info( "Shutting down Memcached CacheManager" );
  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  Log logger = LogFactory.getLog(getClass());
  9.      private  ICacheManager<IMemcachedCache> cacheManager;
  10.      private  String cacheName;
  11.      private  String beanName;
  12.      private  IMemcachedCache cache;
  13.      public   void  setCacheManager(ICacheManager<IMemcachedCache> cacheManager) {
  14.          this .cacheManager = cacheManager;
  15.     }
  16.      public   void  setCacheName(String cacheName) {
  17.          this .cacheName = cacheName;
  18.     }
  19.      public  Object getObject()  throws  Exception {
  20.          return  cache;
  21.     }
  22.      public  Class getObjectType() {
  23.          return   this .cache.getClass();
  24.     }
  25.      public   boolean  isSingleton() {
  26.          return   true
  27.     }
  28.      public   void  setBeanName(String name) {
  29.          this .beanName=name;
  30.     }
  31.      public   void  afterPropertiesSet()  throws  Exception {
  32.          // If no cache name given, use bean name as cache name.
  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(ServletRequest servletRequest, ServletResponse servletResponse,
  2.                          FilterChain filterChain)  throws  IOException, ServletException {
  3.          final  HttpServletRequest hrequest = (HttpServletRequest) servletRequest;
  4.          final  HttpServletResponse hresponse = (HttpServletResponse) servletResponse;
  5.         String uri = hrequest.getRequestURI();
  6.         logger.debug( "开始SNA拦截-----------------"  + uri);
  7.         HttpSession httpSession = hrequest.getSession();
  8.         String sessionId = httpSession.getId();
  9.          //如果是登出,则直接干掉sessionMap
  10.          if  (uri.equals(logoutUrl)) {
  11.             logger.debug( "remove sessionmap:"  + sessionId);
  12.              //在线人数减1
  13.             getCache().addOrDecr( "userCount" , 1 );
  14.             getCache().remove(sessionId);
  15.         }  else  {
  16.             String cookiesessionid = getSessionIdFromCookie(hrequest, hresponse);
  17.              if  (!sessionId.equals(cookiesessionid)) {
  18.                 createCookie(sessionId, hresponse);
  19.                 SessionMap sessionMap = getSessionMap(cookiesessionid);
  20.                  if  (sessionMap !=  null ) {
  21.                     logger.debug( "fail over--------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(HttpSessionBindingEvent event) {
  2.         HttpSession httpSession = event.getSession();
  3.         String attrName = event.getName();
  4.         Object attrValue = event.getValue();
  5.         String sessionId = httpSession.getId();
  6.         logger.debug( "attributeAdded sessionId:"  + sessionId +  "name:"  + attrName +  ",value:"  + attrValue);
  7.         SessionMap sessionMap = 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(HttpSessionBindingEvent event) {
  18.         HttpSession httpSession = event.getSession();
  19.         String attrName = event.getName();
  20.         String sessionId = httpSession.getId();
  21.         logger.debug( "attributeRemoved sessionId:"  + sessionId +  "name:"  + attrName);
  22.         SessionMap sessionMap = 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(HttpSessionBindingEvent event) {
  30.         attributeAdded(event);
  31.     }
 



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

  1. public   void  sessionDestroyed(HttpSessionEvent event) {
  2.         HttpSession httpSession = event.getSession();
  3.         String sessionId = httpSession.getId();
  4.         logger.debug( "session Removed sessionId:"  + sessionId);
  5.         SessionMap sessionMap = getSessionMap(sessionId);
  6.          if  (sessionMap !=  null ) {
  7.             logger.debug( "remove sessionmap:"  + 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 。关键在于对权限的分配。
应用程序本身不用修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值