概述
为什么80%的码农都做不了架构师?>>>
#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
最后
以上就是仁爱汽车为你收集整理的TangYuan之Cache的设计的全部内容,希望文章能够帮你解决TangYuan之Cache的设计所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复