#TangYuan之Cache的设计
1. 从Cache的定义开始
在本章节中,我将和大家一起来分析一下TangYuan中Cache的架构设计,我们还是从使用层面开始。在TangYuan中如果我们需要使用Cache功能,我们首先需要在主配置文件tangyuan-configuration.xml中定义Cache.
示例1:
......
在上面的代码中,我们定义了一个cache
和一个cacheGroup
,在cache1中指定type="local"
,并定义了一些property;在cacheGrou
p中引用了cache1
。那么在TangYuan是如何是如何来描述cache
,并实现这些用户的定义呢?我们先来看类图:
图片1:
其中CacheVo
对应的是<cache>
标签的定义内容,CacheGroupVo
对应的是<cacheGroup>
标签的定义内容,而CacheRefVo
则用来描述cacheGroup
对cache
的引用关系。结合源码,我们可以更清楚的了解其内部实现:
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 Mapproperties; ......}
CacheGroupVo
public class CacheGroupVo extends CacheVo { private ListcacheRefList; 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, Mapproperties);// 放置数据到缓存中void putObject(Object key, Object value, Integer time);// 从缓存中获取数据Object getObject(Object key); // 清除缓存中的数据Object removeObject(Object key);
那么在TangYuan中有多少cache的实现类呢?
图片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) { Mapproperties = 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
看到这里,大家应该明白了,LocalCache
的最底层就是使用了一个HashMap
,在当前JVM内存中进行数据的缓存。
Memcached的实现:
Memcached
和LocalCache
不同,因为Memcached
本身为第三方Cache
,TangYuan中只是做了集成,所以Memcached
初始化的关键在于下面这段代码:
@Overridepublic void start(String resource, Mapproperties) { ...... 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, Mapproperties) { ...... 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, MappropertyMap) { ...... 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:
图中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为例:
流程图:
源码:
@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 *请备注关注的项目
- 邮箱:
- 项目地址: