使用simple-spring-memcached統一緩存的使用
如何在一個中型的Java應用中使用Memcached緩存數據不是個簡單的問題。當某個緩存數據需要在多個系統間共享和失效時,必須要有統一的規劃才能保證不出錯。經過各種實踐,目前系統在使用Memcached緩存數據全部采用Simple-Spring-Memcached框架來完成,并統一規劃各系統Spring和Cache key的配置。
下面對在使用過程中需要注意的點做一個詳細說明:
Cache整體規劃
目前我們系統中有兩個不同的Memcached服務器:
- session memcached服務器:主要存儲用戶的session
- app memcached服務器: 主要用于緩存應用數據
由于應用所有的緩存數據都放在app緩存上,為避免各應用的緩存數據出現沖突,必須規劃好它們的命名空間。所幸Simple-Spring-Memcached支持namespace的概念,因此對各應用的namespace前綴規定如下:
應用 NAMESPACE前綴 goodscenter goodscenter trade trade uic uic
這個namespace在生成key時,將放在最前面,稍后會有例子詳述。
同一個應用中存在許多需要緩存的對象,因此約定namespace前綴之后再加上緩存對象的類名。
例子如下:
應用 緩存對象 完整的NAMESPACE 最終生成的KEY trade TcRate (id為42) trade:TcRate trade:TcRate:12 goodscenter GoodsDo(id為42) goodscenter:GoodsDo goodscenter:GoodsDo:12
key的生成規則
Simple-Spring-Memcached提供的針對單個對象的注解接口提供了兩種key生成方式,詳情見此文
- AssignCache類注解通過assignKey指定cache的key
- SingleCache類注解通過ParameterValueKeyProvider注解指定生成key的方法
對于第一種只要求必須保證key不與其它的沖突,且namesapce符合規則。
第二種時,約定緩存的數據對象必須實現有帶CacheKeyMethod的cacheKey方法,參考實現如下:
@CacheKeyMethod public String cacheKey() { return this.getId(); }

目前@CacheKeyMethod只支持返回String的方法,需要改造成可接受Long,Integer型的。當前必須有單獨的方法來作為緩存Key的生成器

真實存放到Memcached的key的生成規則是:namespace:key。
如goodscenter的id為42的domain對象GoodsDo,按上述方式生成的key為:goodscenter:GoodsDo:42
spring配置說明
關于Simple-Spring-Memcached具體XML配置如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <import resource="classpath:simplesm-context.xml"/> <aop:aspectj -autoproxy/> <context:annotation -config/> <bean name="appCache" class="com.google.code.ssm.CacheFactory"> <property name="cacheClientFactory"> <bean class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/> </property> <property name="addressProvider"> <bean class="com.google.code.ssm.config.DefaultAddressProvider"> <!--memcached服務器ip:port 可以是多個,格式為: 127.0.0.1:11211,192.168.100.11:11211--> <property name="address" value="{memcached.server}"/> </bean> </property> <property name="configuration"> <!-- memcached連接器的配置,具體的配置項參考這個類 --> <bean class="com.google.code.ssm.providers.XMemcachedConfiguration"> <!--是否使用一致性哈希--> <property name="consistentHashing" value="true"/> <!--連接池--> <property name="connectionPoolSize" value="10"/> <property name="optimizeGet" value="true"/> </bean> </property> <property name="cacheName"> <!-- 該Memcached配置的Cache名稱 一個應用中存在多個Memcached時,各個配置的cacheName必須不同。如果該值未設,系統默認為default --> <value>appCache</value> </property> </bean> </beans>
Java代碼中使用說明
a. 注解方式使用
直接使用注解來處理緩存及失效非常簡單,下面是相應的例子:
讀取緩存:
EventGoodsServiceClientImpl.java @Override @ReadThroughSingleCache(namespace = "goodscenter:EventGoodsDo", expiration = 60) @CacheName("appCache") public EventGoodsDo queryEventGoodsDo(@ParameterValueKeyProvider(order = 0) long goodsId, @ParameterValueKeyProvider(order = 1) long eventId) { return getRemoteServiceBean().queryEventGoodsDo(goodsId, eventId); }
更新緩存:
EventGoodsDaoImpl.java@BridgeMethodMappings(value = {@BridgeMethodMapping(erasedParamTypes ={Object.class},targetParamTypes = {com.hqb360.promotion.dao.entity.EventGoods.class},methodName = "update")}) public class EventGoodsDaoImpl<EventGoods> extends BaseDaoImpl<EventGoods> implements EventGoodsDao<EventGoods> { @Override public DaoStatementName getDaoStatementName() { return new DefaultDaoStatementName() { public String getDomainName() { return "EventGoods"; } }; } @Override @InvalidateSingleCache(namespace = "goodscenter:EventGoodsDo") @CacheName("appCache") public void update(@ParameterValueKeyProvider EventGoods obj) throws DataAccessException { super.update(obj); } }
EventGoods.java @CacheKeyMethod public String getCacheKey() { return goodsId + CACHE_ID_SEPARATOR + eventId; } public static final String CACHE_ID_SEPARATOR = "/";
上述代碼需要注意的點
- 多個方法參數都作為cacheKey時,ParameterValueKeyProvider必須指明其order值
- 多個方法參數作為cacheKey時,參數之間在 / 號分隔
- EventGoodsDaoImpl類中的update方法參數接收的是一個泛型對象,因此必須在該類上配置BridgeMethodMappings。具體配置見示例
b. 以bean的方式使用Cache對象
某些場景我們希望更便捷地自己手動來管理緩存數據,此時需要使用Simple-Spring-Memcached配置中定義的bean。以上面的配置文件為例,使用方法如下
bean的注入:
@Autowired private Cache appCache;
bean的使用:
appCache.set(Constants.CACHE_KEY + members.getMemberId(), 3600,cacheValue);
Posted in Java
使用simple-spring-memcached統一緩存的使用
如何在一個中型的Java應用中使用Memcached緩存數據不是個簡單的問題。當某個緩存數據需要在多個系統間共享和失效時,必須要有統一的規劃才能保證不出錯。經過各種實踐,目前系統在使用Memcached緩存數據全部采用Simple-Spring-Memcached框架來完成,并統一規劃各系統Spring和Cache key的配置。
下面對在使用過程中需要注意的點做一個詳細說明:
Cache整體規劃
目前我們系統中有兩個不同的Memcached服務器:
- session memcached服務器:主要存儲用戶的session
- app memcached服務器: 主要用于緩存應用數據
由于應用所有的緩存數據都放在app緩存上,為避免各應用的緩存數據出現沖突,必須規劃好它們的命名空間。所幸Simple-Spring-Memcached支持namespace的概念,因此對各應用的namespace前綴規定如下:
應用 | NAMESPACE前綴 | |
---|---|---|
goodscenter | goodscenter | |
trade | trade | |
uic | uic |
這個namespace在生成key時,將放在最前面,稍后會有例子詳述。
同一個應用中存在許多需要緩存的對象,因此約定namespace前綴之后再加上緩存對象的類名。
例子如下:
應用 | 緩存對象 | 完整的NAMESPACE | 最終生成的KEY |
---|---|---|---|
trade | TcRate (id為42) | trade:TcRate | trade:TcRate:12 |
goodscenter | GoodsDo(id為42) | goodscenter:GoodsDo | goodscenter:GoodsDo:12 |
key的生成規則
Simple-Spring-Memcached提供的針對單個對象的注解接口提供了兩種key生成方式,詳情見此文
- AssignCache類注解通過assignKey指定cache的key
- SingleCache類注解通過ParameterValueKeyProvider注解指定生成key的方法
對于第一種只要求必須保證key不與其它的沖突,且namesapce符合規則。
第二種時,約定緩存的數據對象必須實現有帶CacheKeyMethod的cacheKey方法,參考實現如下:
@CacheKeyMethod public String cacheKey() { return this.getId(); }
![]() | 目前@CacheKeyMethod只支持返回String的方法,需要改造成可接受Long,Integer型的。當前必須有單獨的方法來作為緩存Key的生成器 |
![]() | 真實存放到Memcached的key的生成規則是:namespace:key。 如goodscenter的id為42的domain對象GoodsDo,按上述方式生成的key為:goodscenter:GoodsDo:42 |
spring配置說明
關于Simple-Spring-Memcached具體XML配置如下:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <import resource="classpath:simplesm-context.xml"/> <aop:aspectj -autoproxy/> <context:annotation -config/> <bean name="appCache" class="com.google.code.ssm.CacheFactory"> <property name="cacheClientFactory"> <bean class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/> </property> <property name="addressProvider"> <bean class="com.google.code.ssm.config.DefaultAddressProvider"> <!--memcached服務器ip:port 可以是多個,格式為: 127.0.0.1:11211,192.168.100.11:11211--> <property name="address" value="{memcached.server}"/> </bean> </property> <property name="configuration"> <!-- memcached連接器的配置,具體的配置項參考這個類 --> <bean class="com.google.code.ssm.providers.XMemcachedConfiguration"> <!--是否使用一致性哈希--> <property name="consistentHashing" value="true"/> <!--連接池--> <property name="connectionPoolSize" value="10"/> <property name="optimizeGet" value="true"/> </bean> </property> <property name="cacheName"> <!-- 該Memcached配置的Cache名稱 一個應用中存在多個Memcached時,各個配置的cacheName必須不同。如果該值未設,系統默認為default --> <value>appCache</value> </property> </bean> </beans>
Java代碼中使用說明
a. 注解方式使用
直接使用注解來處理緩存及失效非常簡單,下面是相應的例子:
讀取緩存:
@Override @ReadThroughSingleCache(namespace = "goodscenter:EventGoodsDo", expiration = 60) @CacheName("appCache") public EventGoodsDo queryEventGoodsDo(@ParameterValueKeyProvider(order = 0) long goodsId, @ParameterValueKeyProvider(order = 1) long eventId) { return getRemoteServiceBean().queryEventGoodsDo(goodsId, eventId); }
更新緩存:
@BridgeMethodMappings(value = {@BridgeMethodMapping(erasedParamTypes ={Object.class},targetParamTypes = {com.hqb360.promotion.dao.entity.EventGoods.class},methodName = "update")}) public class EventGoodsDaoImpl<EventGoods> extends BaseDaoImpl<EventGoods> implements EventGoodsDao<EventGoods> { @Override public DaoStatementName getDaoStatementName() { return new DefaultDaoStatementName() { public String getDomainName() { return "EventGoods"; } }; } @Override @InvalidateSingleCache(namespace = "goodscenter:EventGoodsDo") @CacheName("appCache") public void update(@ParameterValueKeyProvider EventGoods obj) throws DataAccessException { super.update(obj); } }
@CacheKeyMethod public String getCacheKey() { return goodsId + CACHE_ID_SEPARATOR + eventId; } public static final String CACHE_ID_SEPARATOR = "/";
上述代碼需要注意的點
- 多個方法參數都作為cacheKey時,ParameterValueKeyProvider必須指明其order值
- 多個方法參數作為cacheKey時,參數之間在 / 號分隔
- EventGoodsDaoImpl類中的update方法參數接收的是一個泛型對象,因此必須在該類上配置BridgeMethodMappings。具體配置見示例
b. 以bean的方式使用Cache對象
某些場景我們希望更便捷地自己手動來管理緩存數據,此時需要使用Simple-Spring-Memcached配置中定義的bean。以上面的配置文件為例,使用方法如下
bean的注入:
@Autowired private Cache appCache;
bean的使用:
appCache.set(Constants.CACHE_KEY + members.getMemberId(), 3600,cacheValue);
Posted in Java