我是靠谱客的博主 仁爱汽车,最近开发中收集的这篇文章主要介绍TangYuan之Cache的设计,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

为什么80%的码农都做不了架构师?>>>   hot3.png

#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;在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<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:

图片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的实现:

MemcachedLocalCache不同,因为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:

图片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

源码:

@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的设计所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(74)

评论列表共有 0 条评论

立即
投稿
返回
顶部