flea-frame-cache使用之整合Memcached和Redis接入

整合Memcached和Redis接入

1. 參考

[flea-frame-cache使用之整合Memcached和Redis接入 源代碼]
flea-frame-cache使用之整合Memcached和Redis接入java

2. 依賴

Memcached-Java-Client-3.0.2.jargit

<!-- Memcached相關 -->
<dependency>
    <groupId>com.whalin</groupId>
    <artifactId>Memcached-Java-Client</artifactId>
    <version>3.0.2</version>
</dependency>

jedis-3.0.1.jargithub

<!-- Java redis -->
<dependency>
     <groupId>redis.clients</groupId>
     <artifactId>jedis</artifactId>
     <version>3.0.1</version>
</dependency>

spring-context-4.3.18.RELEASE.jarredis

<!-- Spring相關 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.18.RELEASE</version>
</dependency>

spring-context-support-4.3.18.RELEASE.jar算法

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.18.RELEASE</version>
</dependency>

3. 基礎接入

通過上兩篇博文的介紹,MemcachedRedis 相信不少筆友都能成功的接入應用系統了。隨着業務的複雜度上升,緩存的應用場景不斷增多,單獨的對接一個緩存系統,已經沒法知足業務發展要求。spring

本文着眼於整合多套緩存接入:一個緩存 cache 對應一個緩存數據 cache-data,一個緩存數據 cache-data 對應一個緩存組 cache-group,多個緩存服務器 cache-server 關聯一個緩存組 cache-group,一個緩存組 cache-group 對應具體的緩存接入實現(目前支持 MemcachedRedis)。緩存

下面且聽我慢慢道來:服務器

3.1 Flea緩存配置文件

Flea緩存配置文件 ( flea-cache-config.xml),用來整合 MemcachedRedis 的相關配置,包含了緩存數據,緩存組,緩存服務器,緩存參數以及其餘緩存配置項。app

<?xml version="1.0" encoding="UTF-8"?>

<flea-cache-config>

    <!-- 緩存初始化配置項集 -->
    <cache-items key="FleaCacheInit" desc="緩存初始化配置項">
        <cache-item key="systemName" desc="緩存所屬系統名">FleaFrame</cache-item>
    </cache-items>

    <!-- Flea緩存建造者配置項集 -->
    <cache-items key="FleaCacheBuilder" desc="Flea緩存建造者實現">
        <cache-item key="MemCached" desc="MemCached的Flea緩存建造者實現">com.huazie.frame.cache.memcached.MemCachedFleaCacheBuilder</cache-item>
        <cache-item key="Redis" desc="Redis的Flea緩存建造者實現">com.huazie.frame.cache.redis.RedisFleaCacheBuilder</cache-item>
    </cache-items>

    <!-- Redis緩存參數集 -->
    <cache-params key="Redis" desc="Redis緩存配置數據">
        <cache-param key="connectionTimeout" desc="Redis客戶端socket鏈接超時時間">2000</cache-param>
        <cache-param key="soTimeout" desc="Redis客戶端socket讀寫超時時間">2000</cache-param>
        <cache-param key="hashingAlg" desc="Redis分佈式hash算法(1:MURMUR_HASH,2:MD5)">1</cache-param>
        <cache-param key="pool.maxTotal" desc="Redis客戶端Jedis鏈接池最大鏈接數">100</cache-param>
        <cache-param key="pool.maxIdle" desc="Redis客戶端Jedis鏈接池最大空閒鏈接數">10</cache-param>
        <cache-param key="pool.minIdle" desc="Redis客戶端Jedis鏈接池最小空閒鏈接數">0</cache-param>
        <cache-param key="pool.maxWaitMillis" desc="Redis客戶端Jedis鏈接池獲取鏈接時的最大等待毫秒數">2000</cache-param>
    </cache-params>

    <!-- MemCached緩存參數集 -->
    <cache-params key="MemCached" desc="MemCached緩存配置數據">
        <cache-param key="initConn" desc="初始化時對每一個服務器創建的鏈接數目">20</cache-param>
        <cache-param key="minConn" desc="每一個服務器創建最小的鏈接數">20</cache-param>
        <cache-param key="maxConn" desc="每一個服務器創建最大的鏈接數">500</cache-param>
        <cache-param key="maintSleep" desc="自查線程週期進行工做,其每次休眠時間">60000</cache-param>
        <cache-param key="nagle" desc="Socket的參數,若是是true在寫數據時不緩衝,當即發送出去">true</cache-param>
        <cache-param key="socketTO" desc="Socket阻塞讀取數據的超時時間">3000</cache-param>
        <cache-param key="socketConnectTO" desc="Socket鏈接超時時間">3000</cache-param>
        <!--
            0 - native String.hashCode();
            1 - original compatibility
            2 - new CRC32 based
            3 - MD5 Based
        -->
        <cache-param key="hashingAlg" desc="MemCached分佈式hash算法">3</cache-param>
    </cache-params>

    <!-- Flea緩存數據集 -->
    <cache-datas>
        <cache-data type="fleaAuth" desc="Flea Auth緩存數據所在組配置">authGroup</cache-data>
        <cache-data type="fleaJersey" desc="Flea Jersey緩存數據所在組配置">configGroup</cache-data>
        <cache-data type="fleaFrame" desc="Flea Frame配置數據所在組配置">configGroup</cache-data>
    </cache-datas>

    <!-- Flea緩存組集 -->
    <cache-groups>
        <cache-group group="authGroup" desc="Flea權限數據緩存組">MemCached</cache-group>
        <cache-group group="configGroup" desc="Flea配置數據緩存組">Redis</cache-group>
    </cache-groups>

    <!-- Flea緩存服務器集 -->
    <cache-servers>
        <cache-server group="authGroup" weight="1" desc="MemCached緩存服務器配置">127.0.0.1:31113</cache-server>
        <cache-server group="authGroup" weight="1" desc="MemCached緩存服務器配置">127.0.0.1:31114</cache-server>

        <cache-server group="configGroup" password="huazie123" weight="1" desc="Redis緩存服務器配置">127.0.0.1:10001</cache-server>
        <cache-server group="configGroup" password="huazie123" weight="1" desc="Redis緩存服務器配置">127.0.0.1:10002</cache-server>
        <cache-server group="configGroup" password="huazie123" weight="1" desc="Redis緩存服務器配置">127.0.0.1:10003</cache-server>
    </cache-servers>

</flea-cache-config>

3.2 Flea緩存定義文件

Flea緩存定義文件(flea-cache.xml),用來定義各種緩存,其中 key 表示緩存主關鍵字, type 表示一類緩存數據,expiry 表示緩存生效時長(單位:秒【0:永久】)。socket

<?xml version="1.0" encoding="UTF-8"?>

<flea-cache>

    <caches>

        <cache key="fleaparadetail" type="fleaFrame" expiry="86400" desc="Flea配置數據緩存" />

        <cache key="fleajerseyi18nerrormapping" type="fleaJersey" expiry="86400" desc="Flea Jersey 國際碼和錯誤碼映射緩存" />
        <cache key="fleajerseyresservice" type="fleaJersey" expiry="86400" desc="Flea Jersey 資源服務緩存" />
        <cache key="fleajerseyresclient" type="fleaJersey" expiry="86400" desc="Flea Jersey 資源客戶端緩存" />
        <cache key="fleajerseyresource" type="fleaJersey" expiry="86400" desc="Flea Jersey 資源緩存" />

        <cache key="fleamenufavorites" type="fleaFrame" expiry="0" desc="Flea菜單收藏夾數據緩存" />

    </caches>

    <!-- 其餘緩存定義配置文件引入 -->
    <cache-files>
        <cache-file>
            <location>flea/cache/flea-auth-cache.xml</location>
            <!-- 不包含指定KEY緩存
            <executions>
                <execution>fleaauthuser</execution>
                <execution>fleaauthprivilege</execution>
            </executions>
            -->
        </cache-file>
    </cache-files>

</flea-cache>

3.3 定義核心Flea緩存類 --- CoreFleaCache

該類一樣繼承抽象Flea緩存 AbstractFleaCache,實現其定義的抽象方法;內部定義成員變量 fleaCache 用於指定具體的 Flea 緩存實現(這個具體的實現,可參考 Memcached接入Redis接入),實現的三個方法 getNativeValueputNativeValuedeleteNativeValue 內部採用具體Flea緩存實現fleaCache相應的方法實現讀緩存、寫緩存,刪緩存;從構造方法可見,fleaCache 經過 FleaCacheFactory.getFleaCache(name) ,從Flea緩存工廠中獲取。

/**
 * <p> 核心Flea緩存類 </p>
 *
 * @author huazie
 */
public class CoreFleaCache extends AbstractFleaCache {

    private AbstractFleaCache fleaCache; // 指定Flea緩存實現

    /**
     * <p> 帶參數構造方法,初始化核心Flea緩存類 </p>
     *
     * @param name 緩存主關鍵字
     */
    public CoreFleaCache(String name) {
        super(name, CacheConfigManager.getExpiry(name));
        // 根據緩存主關鍵字name獲取指定Flea緩存對象
        fleaCache = FleaCacheFactory.getFleaCache(name);
        // 取指定Flea緩存的緩存類型
        cache = fleaCache.getCache();
    }

    @Override
    public Object getNativeValue(String key) {
        return fleaCache.getNativeValue(key);
    }

    @Override
    public void putNativeValue(String key, Object value, long expiry) {
        fleaCache.putNativeValue(key, value, expiry);
    }

    @Override
    public void deleteNativeValue(String key) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug1(new Object() {}, "KEY = {}", key);
        }
        fleaCache.deleteNativeValue(key);
    }

    @Override
    public String getSystemName() {
        // 獲取緩存初始化配置項集之緩存所屬系統名配置項
        CacheItem cacheItem = CacheConfigManager.getCacheItem(FleaCacheConfigConstants.FLEA_CACHE_INIT, FleaCacheConfigConstants.SYSTEM_NAME);
        if (ObjectUtils.isEmpty(cacheItem)) {
            throw new RuntimeException("沒法獲取緩存系統名,請檢查flea-cache-config.xml配置【<cache-item key=" + FleaCacheConfigConstants.SYSTEM_NAME + " >】\"");
        }
        return cacheItem.getValue();
    }
}

3.4 定義Flea緩存工廠類 --- FleaCacheFactory

該類根據緩存名(即緩存主關鍵字)所建立的Flea緩存都存入 ConcurrentMap<String, AbstractFleaCache> 中。newCache 方法主要是根據緩存名查找相關緩存 cache,再找到緩存數據 cache-data,接着找到緩存組 cache-group,最後根據緩存組所屬的緩存系統,查找緩存配置項 cache-item,獲取對應Flea緩存的建造者實現,可參見 flea-cache-config.xml 配置。

/**
 * <p> Flea Cache 工廠類 </p>
 *
 * @author huazie
 * @version 1.0.0
 * @since 1.0.0
 */
public class FleaCacheFactory {

    private static final ConcurrentMap<String, AbstractFleaCache> fleaCacheMap = new ConcurrentHashMap<String, AbstractFleaCache>();

    /**
     * <p> 根據緩存主關鍵字name獲取指定Flea緩存對象 </p>
     *
     * @param name 緩存主關鍵字(對應 flea-cache.xml {@code <cache key="緩存主關鍵字"></cache>})
     * @return Flea緩存對象
     * @since 1.0.0
     */
    public static AbstractFleaCache getFleaCache(String name) {
        if (!fleaCacheMap.containsKey(name)) {
            synchronized (fleaCacheMap) {
                if (!fleaCacheMap.containsKey(name)) {
                    fleaCacheMap.put(name, newFleaCache(name));
                }
            }
        }
        return fleaCacheMap.get(name);
    }

    /**
     * <p> 根據緩存主關鍵字name建立一個Flea緩存對象 </p>
     *
     * @param name 緩存主關鍵字(對應 flea-cache.xml {@code <cache key="緩存主關鍵字"></cache>})
     * @return Flea緩存對象
     * @since 1.0.0
     */
    private static AbstractFleaCache newFleaCache(String name) {
        // 獲取Flea緩存配置信息
        Cache cache = CacheConfigManager.getCache(name);
        if (ObjectUtils.isEmpty(cache)) {
            throw new RuntimeException("沒法初始化Flea緩存,請檢查flea-cache.xml配置【<cache key=" + name + " >】");
        }
        // 獲取Flea緩存歸屬數據配置信息
        CacheData cacheData = CacheConfigManager.getCacheData(cache.getType());
        if (ObjectUtils.isEmpty(cacheData)) {
            throw new RuntimeException("沒法初始化Flea緩存,請檢查flea-cache-config.xml配置【<cache-data type=" + cache.getType() + " >】");
        }
        // 獲取Flea緩存組
        CacheGroup cacheGroup = CacheConfigManager.getCacheGroup(cacheData.getGroup());
        if (ObjectUtils.isEmpty(cacheGroup)) {
            throw new RuntimeException("沒法初始化Flea緩存,請檢查flea-cache-config.xml配置【<cache-group group=" + cacheData.getGroup() + " >】");
        }
        // 獲取緩存系統名
        String cacheSystem = cacheGroup.getCache();
        // 獲取Flea緩存參數
        CacheParams cacheParams = CacheConfigManager.getCacheParams(cacheSystem);
        if (ObjectUtils.isEmpty(cacheParams)) {
            throw new RuntimeException("沒法初始化Flea緩存,請檢查flea-cache-config.xml配置【<cache-params key=" + cacheGroup.getCache() + " >】");
        }
        // 獲取Flea緩存服務器
        List<CacheServer> cacheServerList = CacheConfigManager.getCacheServer(cacheGroup.getGroup());
        if (CollectionUtils.isEmpty(cacheServerList)) {
            throw new RuntimeException("沒法初始化Flea緩存,請檢查flea-cache-config.xml配置【<cache-server group=" + cacheGroup.getGroup() + " >】");
        }
        // 獲取指定緩存系統名對應的Flea緩存建造者
        CacheItem cacheItem = CacheConfigManager.getCacheItem(CacheConstants.FleaCacheConfigConstants.FLEA_CACHE_BUILDER, cacheSystem);
        if (ObjectUtils.isEmpty(cacheItem)) {
            throw new RuntimeException("沒法初始化Flea緩存,請檢查flea-cache-config.xml配置【<cache-item key=" + cacheSystem + " >】");
        }
        // Flea緩存建造者
        String builder = cacheItem.getValue();
        if (ObjectUtils.isEmpty(builder)) {
            throw new RuntimeException("沒法初始化Flea緩存,請檢查flea-cache-config.xml配置【<cache-item key=" + cacheSystem + " ></cache-item>】配置項值不能爲空");
        }
        AbstractFleaCache fleaCache;
        try {
            IFleaCacheBuilder fleaCacheBuilder = (IFleaCacheBuilder) ReflectUtils.newInstance(builder);
            fleaCache = fleaCacheBuilder.build(name, cacheServerList, cacheParams);
        } catch (Exception e) {
            throw new RuntimeException("構建Flea緩存出現異常:\n" + e);
        }
        return fleaCache;
    }

}

3.5 定義Flea緩存建造者接口類 --- IFleaCacheBuilder

/**
 * <p> Flea緩存建造者接口類 </p>
 *
 * @author huazie
 */
public interface IFleaCacheBuilder {

    /**
     * <p> 構建Flea緩存對象 </p>
     *
     * @param name            緩存主關鍵字
     * @param cacheServerList 緩存服務器集
     * @param cacheParams     緩存參數集
     * @return Flea緩存對象
     */
    AbstractFleaCache build(String name, List<CacheServer> cacheServerList, CacheParams cacheParams);

}

3.6 定義Memcached Flea緩存建造者 --- MemCachedFleaCacheBuilder

該類實現 IFleaCacheBuilder,用於構建基於 MemcachedFlea 緩存,即建立一個 MemCachedFleaCache

/**
 * <p> MemCached的Flea緩存建造者實現 </p>
 *
 * @author huazie
 */
public class MemCachedFleaCacheBuilder implements IFleaCacheBuilder {

    @Override
    public AbstractFleaCache build(String name, List<CacheServer> cacheServerList, CacheParams cacheParams) {

        if (CollectionUtils.isEmpty(cacheServerList)) {
            return null;
        }
        // 獲取失效時長
        long expiry = CacheConfigManager.getExpiry(name);
        // 獲取MemCached服務器所在組名
        String group = cacheServerList.get(0).getGroup();
        // 經過組名來獲取 MemCached客戶端類
        MemCachedClient memCachedClient = new MemCachedClient(group);
        // 獲取MemCachedPool,並初始化鏈接池
        MemCachedPool memCachedPool = MemCachedPool.getInstance(group);
        memCachedPool.initialize(cacheServerList, cacheParams);
        // 建立一個MemCached Flea緩存類
        AbstractFleaCache fleaCache = new MemCachedFleaCache(name, expiry, memCachedClient);
        return fleaCache;
    }
}

3.7 定義Redis Flea緩存建造者 --- RedisFleaCacheBuilder

該類實現 IFleaCacheBuilder,用於構建基於Redis的Flea緩存,即建立一個 RedisFleaCache

/**
 * <p> Redis的Flea緩存建造者實現 </p>
 *
 * @author huazie
 */
public class RedisFleaCacheBuilder implements IFleaCacheBuilder {

    @Override
    public AbstractFleaCache build(String name, List<CacheServer> cacheServerList, CacheParams cacheParams) {
        if (CollectionUtils.isEmpty(cacheServerList)) {
            return null;
        }
        // 獲取失效時長
        long expiry = CacheConfigManager.getExpiry(name);
        // 獲取緩存組名
        String group = cacheServerList.get(0).getGroup();
        // 初始化鏈接池
        RedisPool.getInstance(group).initialize(cacheServerList, cacheParams);
        // 獲取Redis客戶端代理類
        RedisClient redisClient = RedisClientProxy.getProxyInstance(group);
        // 建立一個Redis Flea緩存
        AbstractFleaCache fleaCache = new RedisFleaCache(name, expiry, redisClient);
        return fleaCache;
    }
}

3.8 定義核心Flea緩存管理類 --- CoreFleaCacheManager

核心Flea緩存管理類 CoreFleaCacheManager 繼承 AbstractFleaCacheManager ,實現 newCache 方法,用於建立一個核心 Flea 緩存。

/**
 * <p> 核心Flea緩存管理類 </p>
 *
 * @author huazie
 * @version 1.0.0
 * @since 1.0.0
 */
public class CoreFleaCacheManager extends AbstractFleaCacheManager {

    @Override
    protected AbstractFleaCache newCache(String name, long expiry) {
        return new CoreFleaCache(name);
    }
}

3.9 整合接入自測 --- FleaCacheTest

首先,這裏須要按照 Flea緩存配置文件 ( flea-cache-config.xml) 中的緩存服務器 cache-server 中地址部署相應的 Memcached 和 Redis 服務,可參考筆者的 這篇博文

@Test
    public void testCoreFleaCache() {
        try {
            AbstractFleaCacheManager manager = FleaCacheManagerFactory.getFleaCacheManager(CacheEnum.FleaCore.getName());
            AbstractFleaCache cache = manager.getCache("fleaparadetail");
            LOGGER.debug("Cache={}", cache);
            //#### 1.  簡單字符串
//            cache.put("menu1", "huazie");
//            cache.put("menu2", "helloworld");
            cache.get("menu1");
            cache.get("menu2");
//            cache.delete("menu1");
//            cache.clear();
            cache.getCacheKey();
            LOGGER.debug(cache.getCacheName() + ">>>" + cache.getCacheDesc());
        } catch (Exception e) {
            LOGGER.error("Exception:", e);
        }
    }

通過上面的介紹,核心Flea緩存相關的內容,基本上算是講解完畢。在不改變現有業務代碼的基礎上,相關緩存 cache 能夠經過修改其歸屬的緩存數據類型 type,實現各種緩存數據,多種緩存系統之間的無縫遷移。

4. 進階接入

4.1 定義核心Spring緩存類 --- CoreSpringCache

核心 Spring 緩存 CoreSpringCache 一樣繼承抽象 Spring 緩存 AbstractSpringCache,用於對接 Spring;從構造方法可見,該類初始化使用核心Flea緩存類 CoreFleaCache

/**
 * <p> 核心Spring緩存類 </p>
 *
 * @author huazie
 */
public class CoreSpringCache extends AbstractSpringCache {

    /**
     * <p> 帶參數構造方法 </p>
     *
     * @param name      緩存主關鍵字
     * @param fleaCache Flea Cache具體實現
     */
    public CoreSpringCache(String name, IFleaCache fleaCache) {
        super(name, fleaCache);
    }

    /**
     * <p> 帶參數構造方法 </p>
     *
     * @param name 緩存主關鍵字
     */
    public CoreSpringCache(String name) {
        super(name, new CoreFleaCache(name));
    }
}

4.2 定義核心Spring緩存管理類 --- CoreSpringCacheManager

核心 Spring 緩存管理類 CoreSpringCacheManager 繼承抽象 Spring 緩存管理類 AbstractSpringCacheManager,用於對接 Spring;基本實現同核心 Flea 緩存管理類 CoreFleaCacheManager,惟一不一樣在於 newCache 的實現,這邊是 new 一個核心 Spring 緩存 CoreSpringCache

/**
 * <p> 核心Spring緩存管理類 </p>
 *
 * @author huazie
 */
public class CoreSpringCacheManager extends AbstractSpringCacheManager {

    @Override
    protected AbstractSpringCache newCache(String name, long expiry) {
        return new CoreSpringCache(name);
    }
}

4.3 Spring配置

<!-- 配置核心Flea緩存管理類 RedisSpringCacheManager -->
<bean id="coreSpringCacheManager" class="com.huazie.frame.cache.core.CoreSpringCacheManager" />

<!-- 開啓緩存 -->
<cache:annotation-driven cache-manager="coreSpringCacheManager" proxy-target-class="true"/>

4.4 緩存自測

private ApplicationContext applicationContext;

    @Before
    public void init() {
        applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        LOGGER.debug("ApplicationContext={}", applicationContext);
    }

    @Test
    public void testCoreSpringCache() {
        try {
            AbstractSpringCacheManager manager = (CoreSpringCacheManager) applicationContext.getBean("coreSpringCacheManager");
            LOGGER.debug("CoreSpringCacheManager={}", manager);

            AbstractSpringCache cache = manager.getCache("fleaparadetail");
            LOGGER.debug("Cache={}", cache);

            //#### 1.  簡單字符串
//          cache.put("menu1", "huazie");
//            cache.get("menu1");
//            cache.get("menu1", String.class);

            //#### 2.  簡單對象(要是能夠序列化的對象)
//          String user = new String("huazie");
//          cache.put("user", user);
//          LOGGER.debug(cache.get("user", String.class));
            cache.clear();

            //#### 3.  List塞對象
//          List<String> userList = new ArrayList<String>();
//          userList.add("huazie");
//          userList.add("lgh");
//          cache.put("user_list", userList);

//          LOGGER.debug(cache.get("user_list",userList.getClass()).toString());

        } catch (Exception e) {
            LOGGER.error("Exception:", e);
        }
    }

4.5 業務邏輯層接入緩存管理

@Cacheable 使用,value 爲緩存名,也做緩存主關鍵字, key 爲具體的緩存鍵

@Cacheable(value = "fleaparadetail", key = "#paraType + '_' + #paraCode")
public FleaParaDetail getParaDetail(String paraType, String paraCode) throws Exception {

    List<FleaParaDetail> fleaParaDetails = fleaParaDetailDao.getParaDetail(paraType, paraCode);
    FleaParaDetail fleaParaDetailValue = null;

    if (CollectionUtils.isNotEmpty(fleaParaDetails)) {
        fleaParaDetailValue = fleaParaDetails.get(0);
    }

    return fleaParaDetailValue;
}

結語

到目前爲止,整合 MemcachedRedis 接入的工做已經所有完成,相信各位已經可以接入系統~~~