使用Memcached、Spring AOP構建數據庫前端緩存框架

openkk 12年前發布 | 73K 次閱讀 緩存服務器 memcached

上回說到Memcahed的安裝及java客戶端的使用(http://my249645546.iteye.com/blog/1420061),現在我們使用memcachedSpring AOP技術來構建一個數據庫的緩存框架。

數據庫訪問可能是很多網站的瓶頸。動不動就連接池耗盡、內存溢出等。前面已經講到如果我們的網站是一個分布式的大型站點,那么使用memcached實現數據庫的前端緩存是個很不錯的選擇;但如果網站本身足夠小只有一個服務器,甚至是vps的那種,不推薦使用memcached,使用Hibernate或者Mybatis框架自帶的緩存系統就行了。

 

一、開啟memcached服務器端服務

如果已經安裝了memcached服務器端程序,請確認服務器端服務已開啟。

二、引入jar

1.  alisoft-xplatform-asf-cache-2.5.1.jar

2.  commons-logging-1.0.4.jar

3.  hessian-3.0.1.jar

4.  log4j-1.2.9.jar

5.  stax-api-1.0.1.jar

6.  wstx-asl-2.0.2.jar

三、創建memcached客戶端配置文件

<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>

四、創建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);
    }
}

五、使用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>
轉自:http://my249645546.iteye.com/blog/1420778

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