對於網上關於memcached緩存更新策略 數不勝數,但是沒有一遍完整的,看起來都很費勁,由於項目中用到的memcache,自然就想到了memcache緩存更新策略的實現。
你可以把你更新緩存的代碼嵌套你的代碼中,但是這樣很不好,混換了你service的代碼,要是以後再換別的緩存產品,那麼你還要每個類去找,去修改很是麻煩。由於之前是這樣寫的,很是痛苦,所以這次要用spring aop來實現。
在做本次試驗之前 ,首先要準備好memcache,具體安裝步驟請參考:http://www.linuxidc.com/Linux/2012-03/56500.htm
瞭解memcache,請參考:對memcached使用的總結和使用場景 http://www.linuxidc.com/Linux/2012-03/56501.htm
下面說以下具體更新策略的實現思路:
首先我們會定義兩個註解類,來說明是插入(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 [email protected]
- * @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小時過期
- }
- 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 [email protected]
- * @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
- }
- 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
- 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);
- }
- }
- 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 [email protected]
- * @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();
- }
- }
- @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);
- }