其实之前我完全没有接触过oscache,今天突发奇想,准备看看缓存是怎么实现的,google了一下,决定看看oscache的源码,简单的写了个TestCase:
所以我对这个产品的熟悉程度基本为0,各位大虾看了后发现误人子弟请用砖轻拍。
简单了看了下oscache的介绍,发现它支持三种缓存方式:JSP Caching、Request Caching、General-Purpose Cache,今天的文章就是针对最后一种General-Purpose Cache的,在oscache中对应的入口是GeneralCacheAdministrator,就从这里开始吧:

public
GeneralCacheAdministrator(Properties p)
{
super(p);
log.info("Constructed GeneralCacheAdministrator()");
createCache();
}


//
这个是super(p)调用的构造函数

protected
AbstractCacheAdministrator(Properties p)
{
loadProps(p);
initCacheParameters();

if (log.isDebugEnabled()) {
log.debug("Constructed AbstractCacheAdministrator()");
}
}


//
初始化参数

private
void
initCacheParameters()
{
algorithmClass = getProperty(CACHE_ALGORITHM_KEY);

blocking = "true".equalsIgnoreCase(getProperty(CACHE_BLOCKING_KEY));

String cacheMemoryStr = getProperty(CACHE_MEMORY_KEY);

if ((cacheMemoryStr != null) && cacheMemoryStr.equalsIgnoreCase("false")) {
memoryCaching = false;
}

unlimitedDiskCache = Boolean.valueOf(config.getProperty(CACHE_DISK_UNLIMITED_KEY)).booleanValue();
overflowPersistence = Boolean.valueOf(config.getProperty(CACHE_PERSISTENCE_OVERFLOW_KEY))
.booleanValue();

String cacheSize = getProperty(CACHE_CAPACITY_KEY);

try {
if ((cacheSize != null) && (cacheSize.length() > 0)) {
cacheCapacity = Integer.parseInt(cacheSize);
}
} catch (NumberFormatException e) {
log.error("The value supplied for the cache capacity, '" + cacheSize
+ "', is not a valid number. The cache capacity setting is being ignored.");
}
}


//
创建缓存实例

private
void
createCache()
{
log.info("Creating new cache");

// 这里构建缓存时用到的参数都是在父类里的#initCacheParameters()中初始化的
applicationCache = new Cache(isMemoryCaching(), isUnlimitedDiskCache(), isOverflowPersistence(),
isBlocking(), algorithmClass, cacheCapacity);

configureStandardListeners(applicationCache);
}


//
Cache的构造函数

public
Cache(
boolean
useMemoryCaching,
boolean
unlimitedDiskCache,
boolean
overflowPersistence,

boolean
blocking, String algorithmClass,
int
capacity)
{
// 这里说明还是支持自定义的缓存策略的
if (((algorithmClass != null) && (algorithmClass.length() > 0)) && (capacity > 0)) {
try {
cacheMap = (AbstractConcurrentReadCache) Class.forName(algorithmClass).newInstance();
cacheMap.setMaxEntries(capacity);
} catch (Exception e) {
log.error("Invalid class name for cache algorithm class. " + e.toString());
}
}

if (cacheMap == null) {
// 选择一个默认的策略
if (capacity > 0) {// 如果有缓存数目的限制,使用LRU
cacheMap = new LRUCache(capacity);
} else {// 否则使用Unlimited
cacheMap = new UnlimitedCache();
}
}

cacheMap.setUnlimitedDiskCache(unlimitedDiskCache);
cacheMap.setOverflowPersistence(overflowPersistence);
cacheMap.setMemoryCaching(useMemoryCaching);

this.blocking = blocking;
}


//
配置事件的listener

protected
Cache configureStandardListeners(Cache cache)
{
if (config.getProperty(PERSISTENCE_CLASS_KEY) != null) {
cache = setPersistenceListener(cache);
}

if (config.getProperty(CACHE_ENTRY_EVENT_LISTENERS_KEY) != null) {
// Grab all the specified listeners and add them to the cache's
// listener list. Note that listeners that implement more than
// one of the event interfaces will be added multiple times.
CacheEventListener[] listeners = getCacheEventListeners();

for (int i = 0; i < listeners.length; i++) {
// Pass through the configuration to those listeners that
// require it
if (listeners[i] instanceof LifecycleAware) {
try {
((LifecycleAware) listeners[i]).initialize(cache, config);
} catch (InitializationException e) {
log.error("Could not initialize listener '" + listeners[i].getClass().getName()
+ "'. Listener ignored.", e);

continue;
}
}

if (listeners[i] instanceof CacheEntryEventListener) {
cache.addCacheEventListener(listeners[i]);
} else if (listeners[i] instanceof CacheMapAccessEventListener) {
cache.addCacheEventListener(listeners[i]);
}
}
}

return cache;
}
这里对缓存#initCacheParameters()和#configureStandardListeners()里的参数大致了解下,代码里有注释良好的JavaDoc,很容易看懂。
1. cache.algorithm 缓存的策略,具体就是用哪种Map来实现缓存,默认有FIFO,LRU,Unlimited三种,也支持自定义的缓存类型的;
2. cache.blocking 是否等待新数据放入缓存,这个感觉应该是在并发读取数据的时候如果数据在其他线程正处于写入的状态这个线程会wait()直到写入线程完成写入后notifyAll();
3. cache.memory 这个是是否使用内存缓存,这个多数情况下都应该是内存的吧;
4. cache.unlimited.disk 看介绍写的是当对象需要持久化缓存(应该是串行化吧)时,是否使用无限的磁盘空间;
5. cache.persistence.overflow.only 这个说明持久化缓存是不是仅在溢出的方式下开启,字面理解应该是否仅在是memory缓存不足的情况开启持久化缓存;
6. cache.capacity 保存的对象的数目。
7. cache.persistence.class 用于持久化的类名
8. cache.event.listeners 缓存事件监听器,多个listener使用逗号分隔开
初始化大致就这么多,还是看看GeneralCacheAdministrator#putInCache()方法吧,这里#putInCache()有4个,我挑了一个参数最多的:putInCache(String key, Object content, String[] groups, EntryRefreshPolicy policy)
/**
* Puts an object in a cache
*
* @param key The unique key for this cached object
* @param content The object to store
* @param groups The groups that this object belongs to
* @param policy The refresh policy to use
*/

public
void
putInCache(String key, Object content, String[] groups, EntryRefreshPolicy policy)
{
// 直接调用的Cache类的#putInCache
getCache().putInCache(key, content, groups, policy, null);
}


//
Cache的#putInCache()方法

public
void
putInCache(String key, Object content, String[] groups, EntryRefreshPolicy policy,

String origin)
{
// 首先查找这个key在缓存中是否已经存在,没有就创建一个
CacheEntry cacheEntry = this.getCacheEntry(key, policy, origin);

// 判断是否是新创建的缓存
boolean isNewEntry = cacheEntry.isNew();

// [CACHE-118] If we have an existing entry, create a new CacheEntry so
// we can still access the old one later
// 这里如果不是新的缓存也会新建一个CacheEntry,因为老的缓存值在后边也能访问到
if (!isNewEntry) {
cacheEntry = new CacheEntry(key, policy);
}

cacheEntry.setContent(content);
cacheEntry.setGroups(groups);

// 放入缓存
cacheMap.put(key, cacheEntry);

// Signal to any threads waiting on this update that it's now ready for them in the cache!
// 这里会通知其他在等待值的线程结束wait
completeUpdate(key);

// 针对缓存事件的listener发送事件
if (listenerList.getListenerCount() > 0) {
CacheEntryEvent event = new CacheEntryEvent(this, cacheEntry, origin);

if (isNewEntry) {
dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_ADDED, event);
} else {
dispatchCacheEntryEvent(CacheEntryEventType.ENTRY_UPDATED, event);
}
}
}
先简单的看看CacheEntry的构造函数吧,这个最基本:
另外还有#completeUpdate()方法:
这里要注意一个事情,其实在#getFromCache()是可以并发读取缓存数据的,而写入的时候只能一个线程写入,另外在写入的时候,更新实际的缓存和修改更新状态是独立开的,我在实际的测试中也发现了并发量很高的连续读写操作其实在#completeUpdate()方法中是会抛出异常的,不过在实际使用中这个情况应该发生的较少。
这里,整个存入缓存的大致流程就介绍完了,当处理多线程并发写入读取时,很多情况是要和#getFromCache()一起结合看的。