利用Spring AOP 更新memcached 緩存策略的實現

jopen 13年前發布 | 37K 次閱讀 memcached WEB服務/RPC/SOA

對于網上關于memcached緩存更新策略 數不勝數,但是沒有一遍完整的,看起來都很費勁,由于項目中用到的memcache,自然就想到了memcache緩存更新策略的實現。

你可以把你更新緩存的代碼嵌套你的代碼中,但是這樣很不好,混換了你service的代碼,要是以后再換別的緩存產品,那么你還要每個類去找,去修改很是麻煩。由于之前是這樣寫的,很是痛苦,所以這次要用spring aop來實現。

在做本次試驗之前 ,首先要準備好memcache,具體安裝步驟請參考:http://blog.csdn.net/ajun_studio/article/details/6745341     

了解memcache,請參考:對memcached使用的總結和使用場景 

 

下面說以下具體更新策略的實現思路:

首先我們會定義兩個注解類,來說明是插入(Cache)緩存還是刪除(Flush)緩存,這兩個類可以方法上,來對你service需要進行緩存方法操作進行注解標記,注解類內用key的前綴,和緩存的有效時間,接著spring aop 攔截service層含有這個兩個注解的方法,獲得key的前綴+方法明+參數組裝成key值,存入到一張臨時表內,如果是Cache注解的話,先判斷,緩沖中有沒,有從緩存中取得,沒有從數據庫中查詢,然后存緩存,返回結果。如果是Flush注解,說明是刪除緩存,那么首先獲得注解中key值的前綴,查詢庫中所以以這個為前綴的key,查詢出來 ,刪除數據庫中的數據,最后在刪除緩存中所有的符合這些key的緩存,來達到更新緩存。另外這張臨時表,可以做個定時任務日終的時候 ,刪除一些無用的數據。

具體代碼:

 Cache注解

package com.woaika.loan.commons.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 用于在查詢的時候 ,放置緩存信息
 * @author ajun
 * @email zhaojun2066@gmail.com
 * @blog http://blog.csdn.net/ajun_studio
 * 2012-2-27 上午10:42:06
 */
@Target(ElementType.METHOD)   
@Retention(RetentionPolicy.RUNTIME)    
@Documented    
@Inherited 
public @interface Cache {

    String prefix();//key的前綴,如咨詢:zx

    long expiration() default 1000*60*60*2;//緩存有效期 1000*60*60*2==2小時過期


}

Flush注解

 

package com.woaika.loan.commons.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 用于刪除緩存
 * @author ajun
 * @email zhaojun2066@gmail.com
 * @blog http://blog.csdn.net/ajun_studio
 * 2012-2-27 上午10:53:03
 */
@Target(ElementType.METHOD)   
@Retention(RetentionPolicy.RUNTIME)    
@Documented    
@Inherited 
public @interface Flush {
    String prefix();//key的前綴,如咨詢:zx
}

臨時表用于存儲key值

CREATE TABLE `cache_log` (                                   
             `id` bigint(20) NOT NULL AUTO_INCREMENT,                   
             `prefix` varchar(50) DEFAULT NULL COMMENT 'key的前綴',  
             `cache_key` varchar(300) DEFAULT NULL COMMENT 'key值',    
             `add_time` datetime DEFAULT NULL,                          
             PRIMARY KEY (`id`)                                         
           ) ENGINE=MyISAM DEFAULT CHARSET=utf8  

memcache客戶端代碼:需要java_memcached-release_2.6.2.jar

package com.woaika.loan.commons.cache;

import java.util.Date;

import com.danga.MemCached.*; 
import com.woaika.loan.commons.constants.CacheConstant;
public class Memcache {

    static MemCachedClient memCachedClient=null;
    static{

        String[] servers = { CacheConstant.SERVIERS};  
        SockIOPool pool = SockIOPool.getInstance();  

        pool.setServers(servers);  
        pool.setFailover(true);  
     // 設置初始連接數、最小和最大連接數以及最大處理時間 
    /*    pool.setInitConn(5);
        pool.setMinConn(5);
        pool.setMaxConn(250);
        pool.setMaxIdle(1000 * 60 * 60 * 6); */
        pool.setInitConn(10);  
        pool.setMinConn(5);  
        pool.setMaxConn(250);  
        pool.setMaintSleep(30);  // 設置主線程的睡眠時間 
     // 設置TCP的參數,連接超時等 
        pool.setNagle(false);  
        pool.setSocketTO(3000);  
        pool.setAliveCheck(true); 

        pool.initialize();  

        memCachedClient = new MemCachedClient();    
        memCachedClient.setPrimitiveAsString(true);//錕斤拷錕叫伙拷
    }
    public static Object  get(String key)
    {
        return memCachedClient.get(key);
    }
//  public static Map<String,Object> gets(String[] keys)
//  {       
//      return memCachedClient.getMulti(keys);
//  }
    public static boolean set(String key,Object o)
    {
        return memCachedClient.set(key, o);     
    }
    public static boolean set(String key,Object o,Date ExpireTime)
    {       
        return memCachedClient.set(key, o, ExpireTime);
    }
    public static boolean exists(String key)
    {
        return memCachedClient.keyExists(key);
    }
    public static boolean delete(String key)
    {
        return memCachedClient.delete(key);
    }
}

spring AOP代碼 基于注解

package com.woaika.loan.front.common.aop;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.woaika.loan.commons.annotation.Cache;
import com.woaika.loan.commons.annotation.Flush;
import com.woaika.loan.commons.cache.Memcache;
import com.woaika.loan.po.CacheLog;
import com.woaika.loan.service.log.ICacheLogService;

/**
 * 攔截緩存
 * @author ajun
 * @email zhaojun2066@gmail.com
 * @blog http://blog.csdn.net/ajun_studio
 * 2012-3-12 上午10:51:58
 */
@Component
@Aspect
public class CacheAop {

    private ICacheLogService cacheLogService;

    @Resource(name="cacheLogService")
    public void setCacheLogService(ICacheLogService cacheLogService) {
        this.cacheLogService = cacheLogService;
    }


    //定義切面
    @Pointcut("execution(* com.woaika.loan.service..*.*(..))")
    public void cachedPointcut() {

    }

    @Around("cachedPointcut()")
    public Object doAround(ProceedingJoinPoint call){
         Object result = null;
         Method[] methods = call.getTarget().getClass().getDeclaredMethods();  
         Signature signature = call.getSignature();
         MethodSignature methodSignature = (MethodSignature) signature;  
         Method method = methodSignature.getMethod();

         for(Method m:methods){//循環方法,找匹配的方法進行執行
             if(m.getName().equals(method.getName())){
                 if(m.isAnnotationPresent(Cache.class)){
                     Cache cache = m.getAnnotation(Cache.class);
                     if(cache!=null){
                            String tempKey = this.getKey(method, call.getArgs());
                            String prefix = cache.prefix();
                            String key = prefix+"_"+tempKey;
                            result =Memcache.get(key);
                            if(null == result){
                                try {
                                    result = call.proceed();
                                    long expiration = cache.expiration();//1000*60*60*2==2小時過期
                                    Date d=new Date();
                                    d=new Date(d.getTime()+expiration);
                                    Memcache.set(key, result, d);
                                    //將key存入數據庫
                                    CacheLog log = new CacheLog();
                                    log.setPrefix(prefix);
                                    log.setCacheKey(key);
                                    this.cacheLogService.add(log);
                                } catch (Throwable e) {
                                    e.printStackTrace();
                                }
                            }

                        }
                } else  if(method.isAnnotationPresent(Flush.class)){
                     Flush flush = method.getAnnotation(Flush.class);
                     if(flush!=null){
                            String prefix = flush.prefix();
                            List<CacheLog>  logs= cacheLogService.findListByPrefix(prefix);
                             if(logs!=null && !logs.isEmpty()){
                                 //刪除數據庫
                                int rows =  cacheLogService.deleteByPrefix(prefix);
                                if(rows>0){
                                    for(CacheLog log :logs){
                                        if(log!=null){
                                            String key = log.getCacheKey();
                                            Memcache.delete(key);//刪除緩存
                                        }
                                    }
                                }
                             }
                        }
                 }else{
                     try {
                         result = call.proceed();
                        } catch (Throwable e) {
                            e.printStackTrace();
                        }
                 }
                 break;
             }

        }



        return result;
    }

    /**
     * 組裝key值
     * @param method
     * @param args
     * @return
     */
   private String getKey(Method method, Object [] args){
        StringBuffer sb = new StringBuffer(); 
        String methodName = method.getName();
        sb.append(methodName);
        if(args!=null && args.length>0){

            for(Object arg:args){
                sb.append(arg);
            }
        }

        return sb.toString();

   }
}

service層方法添加注解,但是里面代碼沒有添加任何memcache客戶端的代碼,達到降低耦合性:

 

@Cache(prefix=CacheConstant.ANLI,expiration=1000*60*60*10)
    @Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly=true)
    public QueryResult findAll(Integer firstIndex, Integer pageSize) {
        Map condition = new HashMap();
        return loanCaseDao.findByCondition(condition, LoanCase.class, firstIndex, pageSize);
    }

以上是主要代碼的實現思路,希望對同志們的有所幫助,關于spring aop 自行google下就知道了

 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!