flea-frame-cache使用之Memcached接入 源代碼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>
spring-context-4.3.18.RELEASE.jargithub
<!-- 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>
/** * <p> 自定義Cache接口類(主要定義了一些增刪改查的方法) </p> * * @author huazie */ public interface IFleaCache { /** * <p> 讀緩存 </p> * * @param key 數據鍵關鍵字 * @return 數據值 */ Object get(String key); /** * <p> 寫緩存 </p> * * @param key 數據鍵關鍵字 * @param value 數據值 */ void put(String key, Object value); /** * <p> 清空全部緩存 </p> */ void clear(); /** * <p> 刪除指定數據鍵關鍵字對應的緩存 </p> * * @param key 數據鍵關鍵字 */ void delete(String key); /** * <p> 獲取 記錄當前Cache全部數據鍵關鍵字 的Set集合 </p> * * @return 數據鍵key的集合 */ Set<String> getCacheKey(); /** * <p> 獲取緩存所屬系統名 </p> * * @return 緩存所屬系統名 */ String getSystemName(); }
/** * <p> 抽象Flea Cache類 </p> * * @author huazie */ public abstract class AbstractFleaCache implements IFleaCache { private final String name; // 緩存數據主關鍵字 private final long expiry; // 有效期(單位:秒) protected CacheEnum cache; // 緩存實現 public AbstractFleaCache(String name, long expiry) { this.name = name; this.expiry = expiry; } @Override public Object get(String key) { Object value = null; try { value = getNativeValue(getNativeKey(key)); } catch (Exception e) { } return value; } @Override public void put(String key, Object value) { if (ObjectUtils.isEmpty(value)) return; try { putNativeValue(getNativeKey(key), value, expiry); // 將指定Cache的key添加到Set集合,並存於緩存中 addCacheKey(key); } catch (Exception e) { } } @Override public void clear() { Set<String> keySet = getCacheKey(); if (CollectionUtils.isNotEmpty(keySet)) { for (String key : keySet) { deleteNativeValue(getNativeKey(key)); } // 刪除 記錄當前Cache全部數據鍵關鍵字 的緩存 deleteCacheAllKey(); } } @Override public void delete(String key) { try { deleteNativeValue(getNativeKey(key)); // 從 記錄當前Cache全部數據鍵關鍵字 的緩存中 刪除指定數據鍵關鍵字key deleteCacheKey(key); } catch (Exception e) { } } /** * <p> 將指定數據鍵關鍵字{@code key}記錄到當前Cache全部數據鍵關鍵字的緩存中 </p> * * @param key 指定Cache的數據鍵關鍵字 */ private void addCacheKey(String key) { Set<String> keySet = getCacheKey(); if (CollectionUtils.isEmpty(keySet)) { keySet = new HashSet<>(); } if (!keySet.contains(key)) { // 只有其中不存在,才從新設置 keySet.add(key); putNativeValue(getNativeCacheKey(name), keySet, CommonConstants.NumeralConstants.ZERO); } } /** * <p> 從 記錄當前Cache全部數據鍵關鍵字 的緩存中 刪除指定數據鍵關鍵字{@code key} </p> * * @param key 指定Cache的數據鍵關鍵字 */ private void deleteCacheKey(String key) { Set<String> keySet = getCacheKey(); if (CollectionUtils.isNotEmpty(keySet)) { // 存在待刪除的數據鍵關鍵字 if (keySet.contains(key)) { if (CommonConstants.NumeralConstants.INT_ONE == keySet.size()) { deleteCacheAllKey(); // 直接將記錄當前Cache全部數據鍵關鍵字的緩存從緩存中清空 } else { // 將數據鍵關鍵字從Set集合中刪除 keySet.remove(key); // 從新覆蓋當前Cache全部數據鍵關鍵字的緩存信息 putNativeValue(getNativeCacheKey(name), keySet, CommonConstants.NumeralConstants.ZERO); } } } } /** * <p> 刪除 記錄當前Cache全部數據鍵關鍵字 的緩存 </p> */ private void deleteCacheAllKey() { try { deleteNativeValue(getNativeCacheKey(name)); } catch (Exception e) { } } @Override @SuppressWarnings(value = "unchecked") public Set<String> getCacheKey() { Set<String> keySet = null; Object keySetObj = getNativeValue(getNativeCacheKey(name)); if (ObjectUtils.isNotEmpty(keySetObj) && keySetObj instanceof Set) { keySet = (Set<String>) keySetObj; } return keySet; } /** * <p> 獲取緩存值 </p> * * @param key 緩存數據鍵關鍵字 * @return 緩存值 */ public abstract Object getNativeValue(String key); /** * <p> 添加緩存數據 </p> * * @param key 緩存數據鍵關鍵字 * @param value 緩存值 * @param expiry 失效時間(單位:秒) */ public abstract void putNativeValue(String key, Object value, long expiry); /** * <p> 刪除指定緩存數據 </p> * * @param key 緩存數據鍵關鍵字 */ public abstract void deleteNativeValue(String key); /** * <p> 獲取緩存所屬系統名 </p> * * @return 緩存所屬系統名 */ public abstract String getSystemName(); /** * <p> 獲取實際存儲的緩存鍵(緩存所屬系統名 + 緩存名(緩存主關鍵字) + 緩存數據鍵(緩存數據關鍵字)) </p> * * @param key 緩存數據鍵關鍵字 * @return 實際存儲的緩存鍵 */ private String getNativeKey(String key) { return StringUtils.strCat(getNativeCacheKey(name), CommonConstants.SymbolConstants.UNDERLINE, key); } /** * <p> 獲取緩存主鍵(包含緩存所屬系統名 + 緩存名(緩存主關鍵字)) </p> * * @param name 緩存名(緩存主關鍵字) * @return 緩存主鍵(緩存所屬系統名 + 緩存名(緩存主關鍵字)) */ protected String getNativeCacheKey(String name) { return StringUtils.strCat(getSystemName(), CommonConstants.SymbolConstants.UNDERLINE, name); } // ...省略一些get方法 }
該類實現了IFleaCache接口,同時定義了三個抽象方法 :spring
public abstract Object getNativeValue(String key); public abstract void putNativeValue(String key, Object value, long expiry); public abstract void deleteNativeValue(String key);
這三個抽象方法由子類實現具體的讀,寫,刪除緩存的原始操做緩存
該類繼承 AbstractFleaCache,實現Memcached緩存的接入使用;bash
/** * <p> MemCached Flea緩存類 </p> * * @author huazie */ public class MemCachedFleaCache extends AbstractFleaCache { private final MemCachedClient memCachedClient; // MemCached客戶端 /** * <p> 帶參數的構造方法,初始化MemCached Flea緩存類 </p> * * @param name 緩存主關鍵字 * @param expiry 失效時長 * @param memCachedClient MemCached客戶端 */ public MemCachedFleaCache(String name, long expiry, MemCachedClient memCachedClient) { super(name, expiry); this.memCachedClient = memCachedClient; cache = CacheEnum.MemCached; } @Override public Object getNativeValue(String key) { return memCachedClient.get(key); } @Override public void putNativeValue(String key, Object value, long expiry) { memCachedClient.set(key, value, new Date(expiry * 1000)); } @Override public void deleteNativeValue(String key) { memCachedClient.delete(key); } @Override public String getSystemName() { return MemCachedConfig.getConfig().getSystemName(); } }
到這一步爲止,底層的Flea緩存接口和實現已經完成,但目前還不能使用;服務器
/** * <p> MemCached鏈接池 </p> * * @author huazie */ public class MemCachedPool { private String poolName; // 鏈接池名 private MemCachedConfig memCachedConfig; // MemCached 配置信息 private SockIOPool sockIOPool; // MemCached SockIOPool private MemCachedPool() { } /** * <p> 獲取MemCached鏈接池實例 (默認) </p> * * @return MemCached鏈接池實例對象 * @since 1.0.0 */ public static MemCachedPool getInstance() { MemCachedPool memCachedPool = new MemCachedPool(); memCachedPool.memCachedConfig = MemCachedConfig.getConfig(); memCachedPool.sockIOPool = SockIOPool.getInstance(); return memCachedPool; } /** * <p> 獲取MemCached鏈接池實例(指定鏈接池名poolName) </p> * * @param poolName 鏈接池名 * @return MemCached鏈接池實例對象 * @since 1.0.0 */ public static MemCachedPool getInstance(String poolName) { MemCachedPool memCachedPool = new MemCachedPool(); memCachedPool.poolName = poolName; memCachedPool.sockIOPool = SockIOPool.getInstance(poolName); return memCachedPool; } /** * <p> 初始化MemCached鏈接池 </p> */ void initialize() { if (ObjectUtils.isEmpty(memCachedConfig)) { throw new RuntimeException("採用默認初始化,請使用MemCachedPool##getInstance()"); } // ...省略 sockIOPool 初始化代碼 } /** * <p> 初始化MemCached鏈接池 </p> * * @param cacheServerList 緩存服務器集 * @param cacheParams 緩存參數集 */ void initialize(List<CacheServer> cacheServerList, CacheParams cacheParams) { if (StringUtils.isBlank(poolName)) { throw new RuntimeException("採用指定鏈接池名初始化,請使用MemCachedPool##getInstance(String poolName)"); } // ...省略 sockIOPool 初始化代碼 } // ...省略get方法 }
flea-frame-cache 讀取 memcached.properties(Memcached配置文件),用做初始化MemCachedPoolapp
# Memcached配置 # Memcached緩存所屬系統名 memcached.systemName=FleaFrame # Memcached服務器地址 memcached.server=127.0.0.1:31113,127.0.0.1:31114 # Memcached服務器權重分配 memcached.weight=1,1 # 初始化時對每一個服務器創建的鏈接數目 memcached.initConn=20 # 每一個服務器創建最小的鏈接數 memcached.minConn=20 # 每一個服務器創建最大的鏈接數 memcached.maxConn=500 # 自查線程週期進行工做,其每次休眠時間 memcached.maintSleep=60000 # Socket的參數,若是是true在寫數據時不緩衝,當即發送出去 memcached.nagle=true # Socket阻塞讀取數據的超時時間 memcached.socketTO=3000 # Socket鏈接超時時間 memcached.socketConnectTO=3000 # Memcached分佈式hash算法 # 0 - native String.hashCode(); # 1 - original compatibility # 2 - new CRC32 based # 3 - MD5 Based memcached.hashingAlg=3
/** * <p> 自定義抽象緩存管理類 </p> * * @author huazie */ public abstract class AbstractFleaCacheManager { private static final ConcurrentMap<String, AbstractFleaCache> cacheMap = new ConcurrentHashMap<>(); private Map<String, Long> configMap = new HashMap<>(); // 各緩存的時間Map /** * <p> 獲取全部的Flea緩存 </p> * * @return 全部的Flea緩存 */ protected Collection<? extends AbstractFleaCache> loadCaches() { return cacheMap.values(); } /** * <p> 根據指定緩存名獲取緩存對象 </p> * * @param name 緩存名 * @return 緩存對象 */ public AbstractFleaCache getCache(String name) { if (!cacheMap.containsKey(name)) { synchronized (cacheMap) { if (!cacheMap.containsKey(name)) { Long expiry = configMap.get(name); if (ObjectUtils.isEmpty(expiry)) { expiry = CommonConstants.NumeralConstants.ZERO; // 表示永久 configMap.put(name, expiry); } cacheMap.put(name, newCache(name, expiry)); } } } return cacheMap.get(name); } /** * <p> 新建立一個緩存對象 </p> * * @param name 緩存名 * @param expiry 失效時間(單位:秒 其中0:表示永久) * @return 新建的緩存對象 */ protected abstract AbstractFleaCache newCache(String name, long expiry); /** * <p> 設置各緩存失效時間配置Map </p> * * @param configMap 失效時間配置Map */ public void setConfigMap(Map<String, Long> configMap) { this.configMap = configMap; } }
上述 MemCachedFleaCache 使用, 須要初始化 MemCachedPoolsocket
/** * <p> MemCached Flea緩存管理類 </p> * * @author huazie */ public class MemCachedFleaCacheManager extends AbstractFleaCacheManager { private MemCachedClient memCachedClient; // MemCached客戶端類 /** * <p> 新建一個MemCached Flea緩存管理類 </p> */ public MemCachedFleaCacheManager() { memCachedClient = new MemCachedClient(); initPool(); } /** * <p> 新建一個MemCached Flea緩存管理類 </p> * * @param memCachedClient MemCached客戶端 */ public MemCachedFleaCacheManager(MemCachedClient memCachedClient) { this.memCachedClient = memCachedClient; initPool(); } /** * <p> 初始化MemCached鏈接池 </p> */ private void initPool() { MemCachedPool.getInstance().initialize(); } @Override protected AbstractFleaCache newCache(String name, long expiry) { return new MemCachedFleaCache(name, expiry, memCachedClient); } }
好了,到了這一步,Memcached已接入完成,開始自測
首先,這裏須要按照 Memcached配置文件中的地址部署相應的 Memcached服務,可參考筆者的 這篇博文。
下面開始演示咱們的Memcached接入自測:
@Test public void testMemeCachedFleaCache() { try { AbstractFleaCacheManager manager = FleaCacheManagerFactory.getFleaCacheManager(CacheEnum.MemCached.getName()); AbstractFleaCache cache = manager.getCache("fleaparadetail"); LOGGER.debug("Cache={}", cache); //#### 1. 簡單字符串 // cache.put("menu1", "huazie"); // cache.get("menu1"); cache.delete("menu1"); // cache.getCacheKey(); LOGGER.debug(cache.getCacheName() + ">>>" + cache.getCacheDesc()); } catch (Exception e) { LOGGER.error("Exception:", e); } }
該類與 AbstractFleaCache 不一樣之處在於,實現了 Spring 的 Cache 接口,用於對接 Spring,相關配置後面會介紹一下。
/** * <p> 抽象Spring緩存類,實現Spring的Cache 和 自定義的 IFleaCache接口 </p> * * @author huazie */ public abstract class AbstractSpringCache implements Cache, IFleaCache { private final String name; // 緩存主要關鍵字(用於區分) private final IFleaCache fleaCache; // 具體Flea緩存實現 public AbstractSpringCache(String name, IFleaCache fleaCache) { this.name = name; this.fleaCache = fleaCache; } @Override public String getName() { return name; } @Override public IFleaCache getNativeCache() { return fleaCache; } @Override public ValueWrapper get(Object key) { if (ObjectUtils.isEmpty(key)) return null; ValueWrapper wrapper = null; Object cacheValue = get(key.toString()); if (ObjectUtils.isNotEmpty(cacheValue)) { wrapper = new SimpleValueWrapper(cacheValue); } return wrapper; } @Override @SuppressWarnings(value = "unchecked") public <T> T get(Object key, Class<T> type) { if (ObjectUtils.isEmpty(key)) return null; Object cacheValue = get(key.toString()); if (ObjectUtils.isNotEmpty(type) && !type.isInstance(cacheValue)) { return null; } return (T) cacheValue; } @Override public <T> T get(Object key, Callable<T> valueLoader) { return null; } @Override public Object get(String key) { if (StringUtils.isBlank(key)) return null; Object cacheValue = fleaCache.get(key); return cacheValue; } @Override public void put(Object key, Object value) { if (ObjectUtils.isEmpty(key)) return; put(key.toString(), value); } @Override public ValueWrapper putIfAbsent(Object key, Object value) { if (ObjectUtils.isEmpty(key)) return null; ValueWrapper wrapper = null; Object cacheValue = get(key.toString()); if (ObjectUtils.isEmpty(cacheValue)) { put(key.toString(), value); } else { wrapper = new SimpleValueWrapper(cacheValue); } return wrapper; } @Override public void put(String key, Object value) { fleaCache.put(key, value); } @Override public void evict(Object key) { if (ObjectUtils.isEmpty(key)) return; delete(key.toString()); } @Override public void clear() { fleaCache.clear(); } @Override public void delete(String key) { fleaCache.delete(key); } @Override public Set<String> getCacheKey() { return fleaCache.getCacheKey(); } @Override public String getSystemName() { return fleaCache.getSystemName(); } }
MemCachedSpringCache 只定義構造方法,使用 MemCachedFleaCache 做爲具體緩存實現。
/** * <p> MemCached Spring Cache的實現(實現spring的Cache接口)</p> * * @author huazie */ public class MemCachedSpringCache extends AbstractSpringCache { /** * <p> 帶參數的構造方法,初始化MemCached Spring緩存類 </p> * * @param name 緩存主關鍵字 * @param fleaCache 具體緩存實現 */ public MemCachedSpringCache(String name, IFleaCache fleaCache) { super(name, fleaCache); } /** * <p> 帶參數的構造方法,初始化MemCached Spring緩存類 </p> * * @param name 緩存主關鍵字 * @param expiry 失效時長 * @param memCachedClient MemCached客戶端 */ public MemCachedSpringCache(String name, long expiry, MemCachedClient memCachedClient) { this(name, new MemCachedFleaCache(name, expiry, memCachedClient)); } }
該類繼承 AbstractTransactionSupportingCacheManager,用於對接 Spring。
/** * <p> 抽象Spring緩存管理類 </p> * * @author huazie */ public abstract class AbstractSpringCacheManager extends AbstractTransactionSupportingCacheManager { private static final ConcurrentMap<String, AbstractSpringCache> cacheMap = new ConcurrentHashMap<String, AbstractSpringCache>(); private Map<String, Long> configMap = new HashMap<String, Long>(); // 各緩存的時間Map @Override protected Collection<? extends AbstractSpringCache> loadCaches() { return cacheMap.values(); } @Override public AbstractSpringCache getCache(String name) { if(!cacheMap.containsKey(name)) { synchronized (cacheMap) { if (!cacheMap.containsKey(name)) { Long expiry = configMap.get(name); if (expiry == null) { expiry = CommonConstants.NumeralConstants.ZERO; // 表示永久 configMap.put(name, expiry); } cacheMap.put(name, newCache(name, expiry)); } } } return cacheMap.get(name); } /** * <p> 新建立一個緩存對象 </p> * * @param name 緩存名 * @param expiry 失效時間(單位:秒 其中0:表示永久) * @return 新建的緩存對象 */ protected abstract AbstractSpringCache newCache(String name, long expiry); /** * <p> 設置各緩存失效時間配置Map </p> * * @param configMap 失效時間配置Map */ public void setConfigMap(Map<String, Long> configMap) { this.configMap = configMap; } }
該類基本實現同 MemCachedFleaCacheManager,不一樣在於newCache 方法返回一個 MemCachedSpringCache 的對象
/** * <p> Memcached的Spring緩存管理類 </p> * * @author huazie */ public class MemCachedSpringCacheManager extends AbstractSpringCacheManager { private MemCachedClient memCachedClient; // Memcached客戶端類 /** * <p> 新建一個MemCached Spring緩存管理類 </p> */ public MemCachedSpringCacheManager() { memCachedClient = new MemCachedClient(); initPool(); } /** * <p> 新建一個MemCached Spring緩存管理類 </p> * * @param memCachedClient MemCached客戶端 */ public MemCachedSpringCacheManager(MemCachedClient memCachedClient) { this.memCachedClient = memCachedClient; initPool(); } /** * <p> 初始化MemCached鏈接池 </p> */ private void initPool() { MemCachedPool.getInstance().initialize(); } @Override protected AbstractSpringCache newCache(String name, long expiry) { return new MemCachedSpringCache(name, expiry, memCachedClient); } }
<!-- 配置緩存管理MemCachedSpringCacheManager 配置緩存時間 configMap (key緩存對象名稱 value緩存過時時間) --> <bean id="memCachedSpringCacheManager" class="com.huazie.frame.cache.memcached.MemCachedSpringCacheManager"> <property name="configMap"> <map> <entry key="fleaparadetail" value="86400"/> </map> </property> </bean> <!-- 開啓緩存, 此處定義表示由spring接入來管理緩存訪問 --> <cache:annotation-driven cache-manager="memCachedSpringCacheManager" proxy-target-class="true"/>
private ApplicationContext applicationContext; @Before public void init() { applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); LOGGER.debug("ApplicationContext={}", applicationContext); } @Test public void testMemCachedSpringCache() { try { AbstractSpringCacheManager manager = (MemCachedSpringCacheManager) applicationContext.getBean("memCachedSpringCacheManager"); LOGGER.debug("MemCachedCacheManager={}", manager); AbstractSpringCache cache = manager.getCache("fleaparadetail"); LOGGER.debug("Cache={}", cache); Set<String> cacheKey = cache.getCacheKey(); LOGGER.debug("CacheKey = {}", cacheKey); // 緩存清理 // cache.clear(); //#### 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)); //#### 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 的接入工做已經所有完成,下一篇將講解 flea-frame-cache使用之Redis接入,敬請期待哦!!!