[flea-frame-cache使用之整合Memcached和Redis接入 源代碼]
java
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>
通過上兩篇博文的介紹,Memcached 和 Redis 相信不少筆友都能成功的接入應用系統了。隨着業務的複雜度上升,緩存的應用場景不斷增多,單獨的對接一個緩存系統,已經沒法知足業務發展要求。spring
本文着眼於整合多套緩存接入:一個緩存 cache 對應一個緩存數據 cache-data,一個緩存數據 cache-data 對應一個緩存組 cache-group,多個緩存服務器 cache-server 關聯一個緩存組 cache-group,一個緩存組 cache-group 對應具體的緩存接入實現(目前支持 Memcached 和 Redis)。緩存
下面且聽我慢慢道來:服務器
Flea緩存配置文件 ( flea-cache-config.xml),用來整合 Memcached 和 Redis 的相關配置,包含了緩存數據,緩存組,緩存服務器,緩存參數以及其餘緩存配置項。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>
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>
該類一樣繼承抽象Flea緩存 AbstractFleaCache,實現其定義的抽象方法;內部定義成員變量 fleaCache 用於指定具體的 Flea 緩存實現(這個具體的實現,可參考 Memcached接入 和 Redis接入),實現的三個方法 getNativeValue,putNativeValue,deleteNativeValue 內部採用具體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(); } }
該類根據緩存名(即緩存主關鍵字)所建立的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; } }
/** * <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); }
該類實現 IFleaCacheBuilder,用於構建基於 Memcached 的 Flea 緩存,即建立一個 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; } }
該類實現 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; } }
核心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); } }
首先,這裏須要按照 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,實現各種緩存數據,多種緩存系統之間的無縫遷移。
核心 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)); } }
核心 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); } }
<!-- 配置核心Flea緩存管理類 RedisSpringCacheManager --> <bean id="coreSpringCacheManager" class="com.huazie.frame.cache.core.CoreSpringCacheManager" /> <!-- 開啓緩存 --> <cache:annotation-driven cache-manager="coreSpringCacheManager" proxy-target-class="true"/>
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); } }
@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; }
到目前爲止,整合 Memcached 和 Redis 接入的工做已經所有完成,相信各位已經可以接入系統~~~