TangYuan之Cache的设计
#TangYuan之Cache的设计
1. 从Cache的定义开始
在本章节中,我将和大家一起来分析一下TangYuan中Cache的架构设计,我们还是从使用层面开始。在TangYuan中如果我们需要使用Cache功能,我们首先需要在主配置文件tangyuan-configuration.xml中定义Cache.
示例1:
<cache id="cache1" type="local"> <property name="strategy" value="time"/> <property name="survivalTime" value="10"/> <property name="log" value="true"/> </cache> ...... <cacheGroup id="cacheGroup"> <cache ref="cache1" /> <cache ref="cache2" /> </cacheGroup>
在上面的代码中,我们定义了一个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 Map<String, String> properties; ...... }
CacheGroupVo
public class CacheGroupVo extends CacheVo { private List<CacheRefVo> cacheRefList; public CacheGroupVo(String id, boolean defaultCache, List<CacheRefVo> 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<String, String> properties); // 放置数据到缓存中 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) { Map<String, String> 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<Object, Object> cache = new HashMap<Object, Object>(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的实现:
Memcached
和LocalCache
不同,因为Memcached
本身为第三方Cache
,TangYuan中只是做了集成,所以Memcached
初始化的关键在于下面这段代码:
@Override public void start(String resource, Map<String, String> 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的相关操作源码:
@Override public 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); } } @Override public Object getObject(Object key) { return cachedClient.get(parseKey(key)); } @Override public Object removeObject(Object key) { Object result = getObject(key); if (null != result) { cachedClient.delete(parseKey(key)); } return result; }
EhCache的实现:
EhCache的初始化则是通过另一种方式:
@Override public void start(String resource, Map<String, String> 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<String, String> 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
的扩展工具包,感兴趣的朋友可以通过 以下地址了解:https://github.com/xsonorg/redis
3. cache的使用
之前两小节,我们一起分析了cache的定义和cache的实现,现在我们再来分析一下TangYuan是如何实现cache的使用的。我们先来看一下开发者是如何使用cache的:
示例2:
<selectOne id="getUserById" cacheUse="id:cache1; key:${service}; time:1"> SELECT * from user WHERE user_id = #{user_id} </selectOne>
在上述示例中,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为例:
流程图:
源码:
@Override public 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 *请备注关注的项目
- 邮箱:xson_org@126.com
- 项目地址: https://github.com/xsonorg/tangyuan
原文地址:https://my.oschina.net/xson/blog/801662
相关推荐
-
Linux 网络工具详解之 ip tuntap 和 tunctl 创建 tap/tun 设备 服务器
2019-7-2
-
[Golang软件推] Frp内网穿透 服务器
2020-6-22
-
使用redis-trib.rb搭建Redis集群(Ubuntu 16.04) 服务器
2020-6-21
-
Linux 上搭建 Snort+BASE 入侵检测系统 服务器
2020-6-11
-
几种图片格式 服务器
2019-8-19
-
使用acme.sh替换Certbot为自己的网站加密 服务器
2020-6-9
-
ES系列之一文带你避开日期类型存在的坑 服务器
2020-7-5
-
压缩大文件时如何限制CPU使用率?—-几种CPU资源限制方法的测试说明 服务器
2020-6-8
-
树莓派安装docker 服务器
2019-8-29
-
超详细!Nginx 日志配置实践 服务器
2020-6-10