论坛首页 Java企业应用论坛

自定义cache接口实现与缓存框架解耦

浏览 12835 次
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-06-23   最后修改:2010-06-23
OO
目前开源的缓存系统有很多,如memcached,OSCache,Ehcache,JbossCache等,其中 OSCache,Ehcache,JbossCache是用java实现的开源的缓存框架,一个大型项目功能模块多,通常都集成了多个缓存系统。每套缓存框架都有各自的优缺点,随着项目的不断发展,更换缓存系统也是很有可能的,所以跟缓存系统的解耦是非常有必要的。

跟缓存进行解耦只需两个工作:

    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也能轻松替换缓存框架。
  • 大小: 17.5 KB
   发表时间:2010-06-23  
我也做了类似的事情,但是有一些跟cache实现相关的没办法做成interface,比如MemCache中,可以put(String key, Object value, Date expireDate),但EhCache中expireDate是没办法指定的,呵呵。为了兼容各cache,只能取众多cache的交集。比较郁闷
0 请登录后投票
   发表时间: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;
	}
}
0 请登录后投票
   发表时间:2010-06-23  
个人觉得缓存实现切换的必要性不大。
0 请登录后投票
   发表时间:2010-06-23  
我觉的memcached的java客户端代码写的不怎么好。
0 请登录后投票
   发表时间:2010-06-23  
slaser 写道
个人觉得缓存实现切换的必要性不大。

主要看做什么项目了,

比如程序以前跑在一台机器上,现在改成集群方式

那就要改成分布式缓存了。


0 请登录后投票
   发表时间:2010-06-23  
luckaway 写道
slaser 写道
个人觉得缓存实现切换的必要性不大。

主要看做什么项目了,

比如程序以前跑在一台机器上,现在改成集群方式

那就要改成分布式缓存了。



我也碰到这种情况,做一个facebook app,要预留分布式接口,但目前是单机应用。
PS:单机下memcache跟ehcache等缓存比起来差远了,时间比高达100:1,不过前者是跨server的,时间要消耗在网络带宽上.而后者在同个jvm进程内,速度差两个数量级也在情理之中。
0 请登录后投票
   发表时间:2010-06-23  
ehcache 也支持分布式的
0 请登录后投票
   发表时间: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是否在这里不好维护
0 请登录后投票
   发表时间:2010-06-23  
slaser 写道
个人觉得缓存实现切换的必要性不大。

比较同意,就数据库一样,更换数据库的几率很小,除非你刚开始就选错了~
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics