博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
TangYuan之Cache的设计
阅读量:6564 次
发布时间:2019-06-24

本文共 9129 字,大约阅读时间需要 30 分钟。

  hot3.png

#TangYuan之Cache的设计


1. 从Cache的定义开始

在本章节中,我将和大家一起来分析一下TangYuan中Cache的架构设计,我们还是从使用层面开始。在TangYuan中如果我们需要使用Cache功能,我们首先需要在主配置文件tangyuan-configuration.xml中定义Cache.

示例1:

......

在上面的代码中,我们定义了一个cache和一个cacheGroup,在cache1中指定type="local",并定义了一些property;在cacheGroup中引用了cache1。那么在TangYuan是如何是如何来描述cache,并实现这些用户的定义呢?我们先来看类图:

图片1:

图片1

其中CacheVo对应的是<cache>标签的定义内容,CacheGroupVo对应的是<cacheGroup>标签的定义内容,而CacheRefVo则用来描述cacheGroupcache的引用关系。结合源码,我们可以更清楚的了解其内部实现:

CacheVo

public class CacheVo {	public enum CacheType {		LOCAL, EHCACHE, MEMCACHE, REDIS	}	public enum CacheStrategyType {		LRU, FIFO, SOFT, WEAK, TIME	}	protected boolean			group;	private ICache				cache;	private CacheType			type;	private String				resource;	private Map
properties; ......}

CacheGroupVo

public class CacheGroupVo extends CacheVo {	private List
cacheRefList; public CacheGroupVo(String id, boolean defaultCache, List
cacheRefList) { super(id, defaultCache); this.cacheRefList = cacheRefList; this.group = true; } ......}

CacheRefVo

public class CacheRefVo {	private CacheVo		cacheVo;	......}

那么示例1中完整的内存描述应该是这样:

cache1 = new CacheVo{	type = CacheType.LOCAL, 	group = false, 	properties = {		strategy = "time", 		survivalTime = "10" ,		log = "true"	}}cacheGroup = new CacheGroupVo(){	group = true,	cacheRefList = [		cache1, 		cache2	]}

了解了cache定义的实现,接下来我们继续分析。有些细心的朋友可能留意到类图中还一个CacheCreater类和一个ICache接口。那这二者又是作何用途呢?

2. cache的实现

ICache接口是所有Cache实现类的核心接口,他定义了Cache的基本操作,全类名是org.xson.tangyuan.cache.ICache, 其中重要的的方法如下:

// 启动void start(String resource, Map
properties);// 放置数据到缓存中void putObject(Object key, Object value, Integer time);// 从缓存中获取数据Object getObject(Object key); // 清除缓存中的数据Object removeObject(Object key);

那么在TangYuan中有多少cache的实现类呢?

图片2:

图片2

说明:

| 实现类 | 用途及说明 | 分类 | | :-- | :--| :-- | | LocalCache | 本地Cache | LocalCache | | LRUCache | LRU策略Cache包装类 | LocalCache | | FIFOCache | FIFO策略Cache包装类 | LocalCache | | SoftCache | Soft策略Cache包装类 | LocalCache | | WeakCache | Weak策略Cache包装类 | LocalCache | | ScheduledCache | 过期策略Cache包装类 | LocalCache | | SynchronizedCache | 并发访问控制Cache包装类 | LocalCache | | LoggingCache | Cache命中率统计Cache包装类 | LocalCache | | MemcachedCache | Memcached实现类 | MemcachedCache | | EhCacheCache | EhCache实现类 | EhCacheCache | | RedisCache | Redis实现类 | RedisCache |

从上述列表可知,TangYuan中提供了LocalCache的实现和对Memcached、EhCache、Redis的扩展实现。 具体是如何实现的,我们可以从两个方面分析,一个是初始化,另一个是功能调用,下面具体看一下源码

LocalCache的实现:

CacheVo:

public void start() {	if (null != cache) {		cache.start(resource, properties);	} else {		cache = new CacheCreater().newInstance(this);	}	if (null != properties) {		properties.clear();		properties = null;	}}

这里涉及到之前类图中尚未介绍的CacheCreater类,TangYuan中是通过此类,对之前表格中提到的Cache实现类进行实例化的。

CacheCreater

这里会根据CacheType来进行不同分支跳转:

public ICache newInstance(CacheVo cacheVo) {	CacheType type = cacheVo.getType();	if (CacheType.LOCAL == type) {		return newLocalCache(cacheVo);	} else if (CacheType.EHCACHE == type) {		return newEhcache(cacheVo);	} else if (CacheType.MEMCACHE == type) {		return newMemcache(cacheVo);	} else if (CacheType.REDIS == type) {		return newRedisCache(cacheVo);	}	return null;}

这里真正的创建和初始化LocalCache:

private ICache newLocalCache(CacheVo cacheVo) {	Map
properties = cacheVo.getProperties(); ICache localCache = new LocalCache(cacheVo.getId()); CacheStrategyType strategyType = CacheStrategyType.LRU; String strategy = properties.get("strategy"); if (null != strategy) { if ("FIFO".equalsIgnoreCase(strategy)) { strategyType = CacheStrategyType.FIFO; } else if ("SOFT".equalsIgnoreCase(strategy)) { strategyType = CacheStrategyType.SOFT; } else if ("WEAK".equalsIgnoreCase(strategy)) { strategyType = CacheStrategyType.WEAK; } else if ("TIME".equalsIgnoreCase(strategy)) { strategyType = CacheStrategyType.TIME; } } int maxSize = 1024; String _maxSize = properties.get("maxSize"); if (null != _maxSize) { maxSize = Integer.parseInt(_maxSize); } int survivalTime = 10; // 10秒 String _survivalTime = properties.get("survivalTime"); if (null != _survivalTime) { survivalTime = Integer.parseInt(_survivalTime); } // 根据设置 if (CacheStrategyType.LRU == strategyType) { localCache = new LRUCache(localCache, maxSize); } else if (CacheStrategyType.FIFO == strategyType) { localCache = new FIFOCache(localCache, maxSize); } else if (CacheStrategyType.SOFT == strategyType) { localCache = new SoftCache(localCache, maxSize); } else if (CacheStrategyType.WEAK == strategyType) { localCache = new WeakCache(localCache, maxSize); } else if (CacheStrategyType.TIME == strategyType) { localCache = new ScheduledCache(localCache, survivalTime); } localCache = new SynchronizedCache(localCache); // log可选 boolean log = false; String _log = properties.get("log"); if (null != _log) { log = Boolean.parseBoolean(_log); } if (log) { localCache = new LoggingCache(localCache); } return localCache;}

从上面可以看到LocalCache的创建会根据用户在<property>标签中的设置,进行层层的包装。而其最底层的 实现类为LocalCache,我们来看一下他的源码:

public class LocalCache extends AbstractCache {	private Map
cache = new HashMap
(1024); @Override public void putObject(Object key, Object value, Integer time) { cache.put(key, value); } public Object getObject(Object key) { return cache.get(key); } public Object removeObject(Object key) { return cache.remove(key); }}

看到这里,大家应该明白了,LocalCache的最底层就是使用了一个HashMap,在当前JVM内存中进行数据的缓存。

Memcached的实现:

MemcachedLocalCache不同,因为Memcached本身为第三方Cache,TangYuan中只是做了集成,所以Memcached初始化的关键在于下面这段代码:

@Overridepublic void start(String resource, Map
properties) { ...... SockIOPool pool = SockIOPool.getInstance(); pool.setServers(serverlist); pool.setWeights(weights); pool.setInitConn(initialConnections); pool.setMinConn(minSpareConnections); pool.setMaxConn(maxSpareConnections); pool.setMaxIdle(maxIdleTime); pool.setMaxBusyTime(maxBusyTime); pool.setMaintSleep(maintThreadSleep); pool.setSocketTO(socketTimeOut); pool.setSocketConnectTO(socketConnectTO); pool.setFailover(failover); pool.setFailback(failback); pool.setNagle(nagleAlg); pool.setHashingAlg(SockIOPool.NEW_COMPAT_HASH); pool.setAliveCheck(aliveCheck); pool.initialize(); cachedClient = new MemCachedClient(); ......}

TangYuan会根据用户在<property>标签中的设置的参数,在start方法中对Memcached进行初始化,具体的参数名称和直接使用Memcached是一致的。

MemcachedCache的相关操作源码:

@Overridepublic void putObject(Object key, Object value, Integer time) {	Date expiry = null;	if (null == time) {		cachedClient.set(parseKey(key), value);	} else {		expiry = new Date(time.intValue() * 1000L);		cachedClient.set(parseKey(key), value, expiry);	}}@Overridepublic Object getObject(Object key) {	return cachedClient.get(parseKey(key));}@Overridepublic Object removeObject(Object key) {	Object result = getObject(key);	if (null != result) {		cachedClient.delete(parseKey(key));	}	return result;}

EhCache的实现:

EhCache的初始化则是通过另一种方式:

@Overridepublic void start(String resource, Map
properties) { ...... try { InputStream inputStream = Resources.getResourceAsStream(resource); this.cacheManager = CacheManager.create(inputStream); this.cache = cacheManager.getCache(cacheManager.getCacheNames()[0]); } catch (Throwable e) { throw new CacheException(e); }}

EhCacheCache采用这种外部资源文件来进行初始化的方式,主要是考虑兼容大家对EhCache使用习惯。

Redis的实现:

最后来看一下RedisCache的初始化方式:

public void start(String resource, Map
propertyMap) { ...... try { client = JedisClient.getInstance(); Properties properties = new Properties(); InputStream inputStream = Resources.getResourceAsStream(resource); properties.load(inputStream); client.start(properties); } catch (Throwable e) { throw new CacheException(e); }}

RedisCache的初始化也是通过引入外部资源文件,这里使用了一个Redis的扩展工具包,感兴趣的朋友可以通过 以下地址了解:

3. cache的使用

之前两小节,我们一起分析了cache的定义和cache的实现,现在我们再来分析一下TangYuan是如何实现cache的使用的。我们先来看一下开发者是如何使用cache的:

示例2:

SELECT * from user WHERE user_id = #{user_id}

在上述示例中,cacheUse这个属性就标志了当前SQL服务将会使用cache,那我们就从cacheUse这个属性说起。

图3:

图片3

图中CacheUseVo就对应示例2中的cacheUse属性,我们可以看到CacheUseVo类中持有了一个CacheVo,相当于持有了ICache的实现类,因此可通过cacheUse的属性值,进行cache的访问。具体代码如下:

public void putObject(final Object arg, final Object value) {	// 异步操作	TangYuanContainer.getInstance().addAsyncTask(new AsyncTask() {		@Override		public void run() {			String key = buildKey(arg);			cacheVo.putObject(key, value, time, ignore, service);		}	});}public Object getObject(Object arg) {	String key = buildKey(arg);	return cacheVo.getObject(key);}

4. SQL服务 + cache执行流程

最后我们来分析一下,当一个服务使用cache后,对其执行流程的影响,我们还以示例2为例:

流程图:

图片4

源码:

@Overridepublic boolean execute(ServiceContext serviceContext, Object arg) throws Throwable {	// 1. 从cache获取	if (null != cacheUse) {		Object result = cacheUse.getObject(arg);		if (null != result) {			serviceContext.setResult(result);			return true;		}	}		// 2. 服务执行	if (XCO.class == resultType) {		result = sqlContext.executeSelectSetListXCO(this, this.resultMap, fetchSize);	} else {		result = sqlContext.executeSelectSetListMap(this, this.resultMap, fetchSize);	}		// 3. 更新cache	if (null != cacheUse) {		cacheUse.putObject(arg, result);	}	}

到此,本章节的内容就结束了,感兴趣的朋友可以关注TangYuan项目。

  • QQ群:518522232 *请备注关注的项目
  • 邮箱:
  • 项目地址:

转载于:https://my.oschina.net/xson/blog/801662

你可能感兴趣的文章
PS中怎么给图层解锁
查看>>
JSF 中h:link 和h:commandLink和 h:outputLink之间的区别
查看>>
在应用中集成科大讯飞的语音识别技术
查看>>
Android 两种为自定义组件添加属性的使用方法和区别
查看>>
Django学习笔记(1)--第一个项目
查看>>
cocos2dx新建android项目lib拷贝、访问权限等问题集
查看>>
如何构建日均千万PV Web站点 (三) Sharding
查看>>
C# Datatable排序
查看>>
软工学习记3
查看>>
信息安全管理(1):组织的三个层面
查看>>
Eclipse导入Hadoop源码项目及编写Hadoop程序
查看>>
HDU4462(子集枚举)
查看>>
原生JS实现圆周运动
查看>>
SSH实现无密码验证登录
查看>>
文件的读写
查看>>
LC.108.Convert Sorted Array to Binary Search Tree
查看>>
nohup 日志切割
查看>>
[POJ1741]Tree(点分治)
查看>>
nyoj305 表达式求值
查看>>
695. Max Area of Island - Medium
查看>>