memcached資料同步與儲存
阿新 • • 發佈:2019-01-23
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>