1. 程式人生 > >memcached資料同步與儲存

memcached資料同步與儲存

1原理圖
這裡寫圖片描述

2程式碼實現
1)

package cn.zhou.common.web.aop;

import java.io.StringWriter;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import
org.springframework.beans.factory.annotation.Autowired; import com.danga.MemCached.MemCachedClient; import cn.itcast.common.web.aop.MemCachedUtil; import cn.zhou.common.encode.Md5Pwd; import sun.org.mozilla.javascript.internal.IdFunctionCall; /** * 快取memcached資料的切面物件 * * @author Administrator around after */
public class CacheInterceptor { @Autowired private MemCachedClient memCachedClient; @Autowired private Md5Pwd md5Pwd; // 快取伺服器時間 private final int TIMEOUT = 3600; private Integer expiry = TIMEOUT; // 配置環繞方法,呼叫service get*方法前,呼叫doAround方法,之後回到service get* 方法 public
Object doAround(ProceedingJoinPoint pjp) throws Throwable { // 去cemCachedClient檢視有沒有資料 key=包名+類名+方法名+引數(多個) String cacheKey = getCacheKey(pjp); // 如果快取伺服器連線不上,就去service層走資料庫查資料 if (memCachedClient.stats().isEmpty()) { return pjp.proceed();// 回service層,繼續service層的方法 } // 快取伺服器memcached沒有對應鍵的資料 Object object = memCachedClient.get(cacheKey); if (object == null) { // 去service層查到對應資料,並寫到 memcached中 Object proceed = pjp.proceed(); memCachedClient.set(cacheKey, proceed,expiry); return proceed; }else { // 如果有資料,返回伺服器資料並去controller層 return object;// 去controller層 } } /** * aop 後置 * 資料同步 * 當資料有變動的時候,需要同步memcached 中 key 對應的value * 實現方式:刪除對應的key(使用者下次查詢需要訪問doAround方法將變化的資料寫到memcached中 * */ public void doAfter(JoinPoint jp) { // 包名+類名 是同一個類下的資料更新的方法,那麼get *儲存在快取的資料就要更新, //因為都是同一個類,所以key值前面的包名+類名是相同的 // com.danga.MemCached.MemCachedClient String PackageName =jp.getTarget().getClass().getName(); PackageName=md5Pwd.encode(PackageName); //獲取memcached的所有key Map<String, Object> keySet = MemCachedUtil.getKeySet(memCachedClient); Set<Entry<String, Object>> entrySet = keySet.entrySet(); for (Entry<String, Object> entry : entrySet) { //遍歷嗎每個key, if(entry.getKey().startsWith(PackageName)){ memCachedClient.delete(entry.getKey()); } } } /** * MemCached key 的命名規則 key=包名+類名+方法名+引數(多個) * * @param pjp * @return */ public String getCacheKey(ProceedingJoinPoint pjp) { StringBuilder sb = new StringBuilder(); StringBuilder sb2 = new StringBuilder(); // 包名 // com.danga.MemCached.MemCachedClient String PackageName = pjp.getTarget().getClass().getName(); // 方法名 String methodName = pjp.getSignature().getName(); if (methodName != null) { sb.append("." + methodName); } // 引數(多個) ObjectMapper om = new ObjectMapper(); // 去掉json中value=null的key 和value om.setSerializationInclusion(Inclusion.NON_NULL); Object[] args = pjp.getArgs(); for (Object arg : args) { // 物件轉json,寫的過程,json 是隻字串流 StringWriter out = new StringWriter(); try { om.writeValue(out, arg); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } sb.append("." + out.toString()); } //memcached key 的長度有限制--255,所以為了防止超過長度,這裡用md5加密==》32位長度 //加密報名+類名===》32 sb2.append(md5Pwd.encode(PackageName)); //方法名+引數多個===》32 sb2.append(md5Pwd.encode(sb.toString())); //返回的是64位的加密數字 return sb2.toString(); } public void setExpiry(Integer expiry) { this.expiry = expiry; } }

2)配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    <!-- memcached配置 -->
    <!-- 配置客戶端 -->
    <bean id="memCachedClient" class="com.danga.MemCached.MemCachedClient">
        <!-- 設定連線池 -->
        <constructor-arg>
            <value>sockIOPool</value>
        </constructor-arg>
    </bean>
    <!--memcached連線池,幫助其例項化 -->
    <!-- factory-method="getInstance" 例項化 -->
    <!-- init-method="initialize" 初始化 -->
    <!-- destroy-method="shutDown" 銷燬 -->
    <bean id="sockIOPool" class="com.danga.MemCached.SockIOPool"
        factory-method="getInstance" init-method="initialize" destroy-method="shutDown">
        <!-- 將自己的引用以構造的形式注入自己類 -->
        <constructor-arg>
            <value>sockIOPool</value>
        </constructor-arg>
        <property name="servers">
            <list>
                <!-- 配置memcached伺服器的地址,如果有多個伺服器,重複寫value -->
                <value>192.168.44.128:11211</value>
            </list>
        </property>
        <!-- 設定權重 ,權重與上面memcached伺服器地址一一對應 -->
        <property name="weights">
            <list>
                <value>1</value>
            </list>
        </property>
    </bean>
    <!-- memcached切面物件 -->
    <bean id="cacheInterceptor" class="cn.zhou.common.web.aop.CacheInterceptor">
        <property name="expiry" value="1800"></property>
    </bean>

    <!-- spring aop 配置 get* 環繞 -->
    <aop:config>
        <!--面 -->
        <aop:aspect ref="cacheInterceptor">
            <!-- 點 -->
            <!-- 切點為service層的get* 方法 -->
            <!-- *cn.zhou.core.servi 會報錯誤 Caused by: java.lang.IllegalArgumentException: 
                Pointcut is not well-formed,要改為* cn.zhou.c -->

                <!-- 資料查詢的快取 -->
            <aop:around method="doAround" pointcut="execution(* cn.zhou.core.service.*.*.get*(..))" />

                <!-- 資料更新,更新memcached ,方法是刪掉查詢的時候儲存的key -->
            <aop:after method="doAfter" pointcut="execution(* cn.zhou.core.service.*.*.update*(..))" />
            <aop:after method="doAfter" pointcut="execution(* cn.zhou.core.service.*.*.add*(..))" />
            <aop:after method="doAfter" pointcut="execution(* cn.zhou.core.service.*.*.delete*(..))" />
        </aop:aspect>

    </aop:config>
</beans>