锁定老帖子 主题:自定义cache接口实现与缓存框架解耦
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-06-23
最后修改:2010-06-23
跟缓存进行解耦只需两个工作: 1. 自定义缓存接口 2. 实现各个缓存框架的adapter(适配器模式)。 UML图如下: 这里cache接口为什么取Store呢?因为有数据不仅仅只存放在内存里,也有bdb、memcachedb等key-value持久存储系统,所以我就把它定义成存储接口 类图里的EhcacheStore,MemcachedStore分别是Ehcache和Memcached的缓存系统的适配类 SimpleCache是自己用HashMap实现一个的简单缓存类(存放小数据,就是配置内容比其他缓存框架要少) EhcacheStore的适配器EhcacheStore: EhcacheStore.java package com.dukuai.donald.store; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; import org.springframework.util.Assert; /** * {@link Ehcache}的适配器。 作用跟{@link EhCacheManagerFactoryBean}相同 * */ public class EhcacheStore<K, V> implements Store<K, V> { private CacheManager cacheManager = null; private String name; private Ehcache ehcache = null; public void init() { Assert.hasText(name, "the name of EhCacheMetisStore must have text"); Assert.notNull(cacheManager, "this cacheManager must not be null"); Assert.isTrue(cacheManager.cacheExists(name), "ehcache whose cacheName is " + name + " is not configured"); this.ehcache = cacheManager.getEhcache(name); } @Override public void clear() { ehcache.removeAll(); } @Override public V get(K k) { Element element = ehcache.get(k); return element == null ? null : (V) element.getValue(); } @Override public void put(K k, V v) { ehcache.put(new Element(k, v)); } @Override public void remove(K k) { ehcache.remove(k); } @Override public int size() { return ehcache.getSize(); } /** spring inject */ public void setCacheManager(CacheManager cacheManager) { this.cacheManager = cacheManager; } @Override public void setName(String name) { this.name = name; } } 这样一来只有适配类里用到了net.sf.ehcache.Ehcache,项目的其他地方都用Store接口进行存储,其他地方不需要不关心底层是用 ehcache、memcached或者自定义的SimpleCache。 再借助spring框架的IOC特性,我们可以很轻易实现的缓存系统的替换。如果没引入spring框架,自己实现StoreFactory也能轻松替换缓存框架。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-06-23
我也做了类似的事情,但是有一些跟cache实现相关的没办法做成interface,比如MemCache中,可以put(String key, Object value, Date expireDate),但EhCache中expireDate是没办法指定的,呵呵。为了兼容各cache,只能取众多cache的交集。比较郁闷
|
|
返回顶楼 | |
发表时间:2010-06-23
最后修改:2010-06-23
那倒也是,实际中put(String key, Object value, Date expireDate)这个不太用的到
我是直接根据有效时间计算出expireDate值的 代码如下 public class MemStore<K, V> implements Storable<K, V> { private static final String DEFAULT_NAME = "default"; /** * cache命名空间,防止key冲突。 不同的name使用不同的{@link SockIO} */ private String name = null; // memcached的server列表 private List<String> serverList = null; // 缓存对象的存活时间 private long liveSecond = 0; private MemCachedClient memCachedClient = null; private SockIOPool pool = null; private boolean initialized = false; public MemStore() { } public MemStore(String name, List<String> serverList) { this.name = name; this.serverList = serverList; } public void init() { // } public void destroy() { pool.shutDown(); } private String generateKey(String name, K k) { if (!initialized) throw new IllegalStateException("must be initialized MemMetisStore at first"); if (name == null || name.trim().length() == 0) throw new IllegalArgumentException("this name must not be empty"); if (k == null) throw new IllegalArgumentException("this k must not be null"); if (k.toString().indexOf("___") != -1) throw new IllegalArgumentException("this k must be contain \"___\""); return (new StringBuilder(name).append("___").append(k)).toString(); } @Override public V get(K k) { if (!initialized) throw new IllegalStateException("must be initialized MemMetisStore at first"); return (V) memCachedClient.get(generateKey(name, k)); } @Override public void put(K k, V v) { if (!initialized) throw new IllegalStateException("must be initialized MemMetisStore at first"); if (liveSecond == 0) { memCachedClient.set(generateKey(name, k), v); } else { long now = System.currentTimeMillis(); Date expiredDate = new Date(liveSecond * 1000l + now); memCachedClient.set(generateKey(name, k), v, expiredDate); } } @Override public void remove(K k) { if (!initialized) throw new IllegalStateException("must be initialized MemMetisStore at first"); memCachedClient.delete(generateKey(name, k)); } @Override public void clear() { throw new UnsupportedOperationException(""); } @Override public int size() { throw new UnsupportedOperationException(""); } /** spring inject */ @Override public void setName(String name) { this.name = name; } public void setServerList(List<String> serverList) { this.serverList = serverList; } public void setLiveSecond(long liveSecond) { this.liveSecond = liveSecond; } } |
|
返回顶楼 | |
发表时间:2010-06-23
个人觉得缓存实现切换的必要性不大。
|
|
返回顶楼 | |
发表时间:2010-06-23
我觉的memcached的java客户端代码写的不怎么好。
|
|
返回顶楼 | |
发表时间:2010-06-23
slaser 写道 个人觉得缓存实现切换的必要性不大。
主要看做什么项目了, 比如程序以前跑在一台机器上,现在改成集群方式 那就要改成分布式缓存了。 |
|
返回顶楼 | |
发表时间:2010-06-23
luckaway 写道 slaser 写道 个人觉得缓存实现切换的必要性不大。
主要看做什么项目了, 比如程序以前跑在一台机器上,现在改成集群方式 那就要改成分布式缓存了。 我也碰到这种情况,做一个facebook app,要预留分布式接口,但目前是单机应用。 PS:单机下memcache跟ehcache等缓存比起来差远了,时间比高达100:1,不过前者是跨server的,时间要消耗在网络带宽上.而后者在同个jvm进程内,速度差两个数量级也在情理之中。 |
|
返回顶楼 | |
发表时间:2010-06-23
ehcache 也支持分布式的
|
|
返回顶楼 | |
发表时间:2010-06-23
luckaway 写道 那倒也是,实际中put(String key, Object value, Date expireDate)这个不太用的到
我是直接根据有效时间计算出expireDate值的 代码如下 public class MemStore<K, V> implements Storable<K, V> { private static final String DEFAULT_NAME = "default"; /** * cache命名空间,防止key冲突。 不同的name使用不同的{@link SockIO} */ private String name = null; // memcached的server列表 private List<String> serverList = null; // 缓存对象的存活时间 private long liveSecond = 0; private MemCachedClient memCachedClient = null; private SockIOPool pool = null; private boolean initialized = false; public MemStore() { } public MemStore(String name, List<String> serverList) { this.name = name; this.serverList = serverList; } public void init() { // } public void destroy() { pool.shutDown(); } private String generateKey(String name, K k) { if (!initialized) throw new IllegalStateException("must be initialized MemMetisStore at first"); if (name == null || name.trim().length() == 0) throw new IllegalArgumentException("this name must not be empty"); if (k == null) throw new IllegalArgumentException("this k must not be null"); if (k.toString().indexOf("___") != -1) throw new IllegalArgumentException("this k must be contain \"___\""); return (new StringBuilder(name).append("___").append(k)).toString(); } @Override public V get(K k) { if (!initialized) throw new IllegalStateException("must be initialized MemMetisStore at first"); return (V) memCachedClient.get(generateKey(name, k)); } @Override public void put(K k, V v) { if (!initialized) throw new IllegalStateException("must be initialized MemMetisStore at first"); if (liveSecond == 0) { memCachedClient.set(generateKey(name, k), v); } else { long now = System.currentTimeMillis(); Date expiredDate = new Date(liveSecond * 1000l + now); memCachedClient.set(generateKey(name, k), v, expiredDate); } } @Override public void remove(K k) { if (!initialized) throw new IllegalStateException("must be initialized MemMetisStore at first"); memCachedClient.delete(generateKey(name, k)); } @Override public void clear() { throw new UnsupportedOperationException(""); } @Override public int size() { throw new UnsupportedOperationException(""); } /** spring inject */ @Override public void setName(String name) { this.name = name; } public void setServerList(List<String> serverList) { this.serverList = serverList; } public void setLiveSecond(long liveSecond) { this.liveSecond = liveSecond; } } put(String key, Object value, long expireDate)不好么? 跨系统的key是否在这里不好维护 |
|
返回顶楼 | |
发表时间:2010-06-23
slaser 写道 个人觉得缓存实现切换的必要性不大。
比较同意,就数据库一样,更换数据库的几率很小,除非你刚开始就选错了~ |
|
返回顶楼 | |