http://my249645546.iteye.com/blog/1420778前端
上回說到Memcahed的安裝及java客戶端的使用(http://my249645546.iteye.com/blog/1420061),如今咱們使用memcached、Spring AOP技術來構建一個數據庫的緩存框架。java
數據庫訪問多是不少網站的瓶頸。動不動就鏈接池耗盡、內存溢出等。前面已經講到若是咱們的網站是一個分佈式的大型站點,那麼使用memcached實現數據庫的前端緩存是個很不錯的選擇;但若是網站自己足夠小隻有一個服務器,甚至是vps的那種,不推薦使用memcached,使用Hibernate或者Mybatis框架自帶的緩存系統就好了。spring
1、開啓memcached服務器端服務數據庫
若是已經安裝了memcached服務器端程序,請確認服務器端服務已開啓。apache
2、引入jarapi
1. alisoft-xplatform-asf-cache-2.5.1.jar緩存
2. commons-logging-1.0.4.jar服務器
3. hessian-3.0.1.jarapp
4. log4j-1.2.9.jar框架
5. stax-api-1.0.1.jar
6. wstx-asl-2.0.2.jar
3、建立memcached客戶端配置文件
<?xml version="1.0" encoding="UTF-8"?>
<memcached> <!-- name 屬性是程序中使用Cache的惟一標識;socketpool 屬性將會關聯到後面的socketpool配置; --> <client name="mclient_0" compressEnable="true" defaultEncoding="UTF-8" socketpool="pool_0"> <!-- 可選,用來處理出錯狀況 --> <errorHandler>com.alisoft.xplatform.asf.cache.memcached.MemcachedErrorHandler </errorHandler> </client> <!-- name 屬性和client 配置中的socketpool 屬性相關聯。 maintSleep屬性是後臺線程管理SocketIO池的檢查間隔時間,若是設置爲0,則代表不須要後臺線程維護SocketIO線程池,默認須要管理。 socketTO 屬性是Socket操做超時配置,單位ms。 aliveCheck 屬性表示在使用Socket之前是否先檢查Socket狀態。 --> <socketpool name="pool_0" maintSleep="5000" socketTO="3000" failover="true" aliveCheck="true" initConn="5" minConn="5" maxConn="250" nagle="false"> <!-- 設置memcache服務端實例地址.多個地址用","隔開 --> <servers>127.0.0.1:11211</servers> <!-- 可選配置。代表了上面設置的服務器實例的Load權重. 例如 <weights>3,7</weights> 表示30% load 在 10.2.224.36:33001, 70% load 在 10.2.224.46:33001 <weights>3,7</weights> --> </socketpool> </memcached>
4、建立memcached客戶端程序
客戶端工具類:
package com.hl.usersmanager.memcached.client; import com.alisoft.xplatform.asf.cache.ICacheManager; import com.alisoft.xplatform.asf.cache.IMemcachedCache; import com.alisoft.xplatform.asf.cache.memcached.CacheUtil; import com.alisoft.xplatform.asf.cache.memcached.MemcachedCacheManager; public class MemcachedCache { private ICacheManager<IMemcachedCache> manager; private IMemcachedCache cache; public MemcachedCache(){ manager = CacheUtil.getCacheManager(IMemcachedCache.class, MemcachedCacheManager.class.getName()); manager.setConfigFile("memcached.xml"); manager.setResponseStatInterval(5*1000); manager.start(); cache = manager.getCache("mclient_0"); } /** * 獲取緩存接口 * @return */ public IMemcachedCache getCache(){ return cache; } /** * 數據放入緩存 * @param key * @param object */ public void put(String key,Object object){ cache.put(key, object); } /** * 從緩存中讀取數據 * @param key * @return */ public Object get(String key){ return cache.get(key); } }
5、使用Spring AOP在數據查詢的Service層實現數據緩存及讀取
實現數據緩存的過程很簡單,就是在Service層查詢數據庫操做前判斷要查詢的數據在緩存中是否存在,若是不存在就到數據庫中查詢,查詢完成後將數據放入緩存系統;若是要查詢的數據在緩存中已經存在,則直接從緩存中讀取,不須要操做數據庫。這就大大下降了數據庫的鏈接次數。原理就是這麼簡單。
可是,若是直接對Service層代碼進行修改,就違背了「開放-封閉」原則,也會致使緩存系統的操做代碼散落到Service層的各處,不方便代碼的管理和維護。因此,Spring AOP華麗登場了。它使用非入侵式的來建立、管理這些緩存操做代碼。
關於Spring AOP自己的一些知識,咱們這裏不作講述。參考資料:
因爲首先要判斷查詢數據是否存在於緩存系統,若是存在直接從緩存中讀取,也就是說Service層的查詢代碼根本不會執行;另外一方面,若是數據在緩存系統中不存在,從數據庫查詢出的結果,咱們須要將其放入緩存系統中。
咱們來看Spring AOP的幾個裝備中哪一個適用呢?那就是最強大的環繞通知裝備@Around!
下面以UserService爲例,其源代碼以下:
package com.hl.usersmanager.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.hl.usersmanager.dao.IUserMapper; import com.hl.usersmanager.model.Users; import com.hl.usersmanager.service.IUserService; //使用Service註解 不須要再在配置文件中配置bean @Service public class UserServiceImpl implements IUserService{ @Autowired private IUserMapper userMapper; @Override @Transactional public Users findUserByName(String name) { return userMapper.findUserByName(name); } …… }
findUserByName主要實現按照用戶名查詢用戶的功能,如今咱們使用Spring AOP來實現緩存:
package com.hl.usersmanager.aop.service; import org.apache.log4j.Logger; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import com.hl.usersmanager.memcached.client.MemcachedCache; import com.hl.usersmanager.model.Users; @Aspect public class UserServiceInterceptor { public static final Logger log = Logger .getLogger(UserServiceInterceptor.class); //將緩存客戶端工具類 MemcachedCache 織入進來 @Autowired private MemcachedCache memcachedCache; /* * 定義pointcunt */ @Pointcut("execution(* com.hl.usersmanager.service.impl.UserServiceImpl.*(..))") public void aPointcut() { } /** * 環繞裝備 用於攔截查詢 若是緩存中有數據,直接從緩存中讀取;不然從數據庫讀取並將結果放入緩存 * * @param call * @param name * @return */ @Around("aPointcut()&&args(name)") public Users doFindUserByNameAround(ProceedingJoinPoint call, String name) { Users users = null; if (memcachedCache.getCache().containsKey("findUserByName_" + name)) { users = (Users) memcachedCache.get("findUserByName_" + name); log.debug("從緩存中讀取!findUserByName_" + name); } else { try { users = (Users) call.proceed(); if (users != null) { memcachedCache.put("findUserByName_" + name, users); log.debug("緩存裝備被執行:findUserByName_" + name); } } catch (Throwable e) { e.printStackTrace(); } } return users; } }
環繞通知裝備須要一個ProceedingJoinPoint 類型的參數,它的強大之處在於能夠代理一個咱們的切入點,指定切入點方法是否執行,或者獲取執行後的返回結果!!
memcachedCache.getCache().containsKey("findUserByName_" + name)
能夠判斷緩存中是否有指定的數據。若是有則直接從緩存中讀取:
users = (Users) memcachedCache.get("findUserByName_" + name);
不然調用切入點UserServiceImpl的findUserByName方法:
users = (Users) call.proceed();
call.proceed()表示執行切入點的方法。
使用Spring AOP之後,整個緩存系統代碼看起來 就是這麼優雅!UserServiceImpl根本不知道外界發了什麼,更不知道外界調用它的findUserByName的時候已經被攔截了!
那天不用緩存系統,只須要將Aop這塊的代碼去掉便可。
固然,咱們還須要在Spring 配置文件中註冊一個memcached客戶端工具類的bean:
<!-- MemcachedCache緩存 --> <bean id="MemcachedCache" class="com.hl.usersmanager.memcached.client.MemcachedCache"></bean>